Skip to content
Merged
Show file tree
Hide file tree
Changes from 2 commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion Lib/argparse.py
Original file line number Diff line number Diff line change
Expand Up @@ -1963,7 +1963,7 @@ def add_subparsers(self, **kwargs):
positionals = self._get_positional_actions()
groups = self._mutually_exclusive_groups
formatter.add_usage(None, positionals, groups, '')
kwargs['prog'] = formatter.format_help().strip()
kwargs['prog'] = formatter._decolor(formatter.format_help().strip())
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Just to make sure this removes to the coloring independent if coloring is on or not, yes?

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Well, _decolor strips ANSI codes when coloring is enabled. When coloring is disabled, it's a no-op since no ANSI codes should exist.

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

That's not going to be enough. I mentioned before, but prog is a public metadata on the parser that contains the name of the application. We should not color it until it's about to be printed later on, otherwise will not fix my issue here https://github.com/tox-dev/sphinx-argparse-cli/blob/main/src/sphinx_argparse_cli/_logic.py#L340-L342. Thanks!

Copy link
Member Author

@savannahostrowski savannahostrowski Oct 9, 2025

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Hmm, I see your point. I think another approach here could be to create a separate formatter explicitly set with color=False, so that prog wouldn't get colored in the first place (rather than having to decolor it).

Copy link
Contributor

@gaborbernat gaborbernat Oct 9, 2025

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Unrelated but I should mention it here that I was surprised that setting color to False for the formatter (see here https://github.com/tox-dev/sphinx-argparse-cli/blob/main/src/sphinx_argparse_cli/_logic.py#L402-L404) doesn't actually disables color printing, had to set NO_COLOR during format_usage to not color that output 🤔 Seems the color argument of the parser is instead used 🤔 not sure if that's a bug or a feature, but suprised me.

By the way I love the colors in the CLI ❤️ (definitely the best feature of 3.14) , just they are not needed when you're writing a sphinx plugin to render it for other outputs 😆

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Hmm, this seems like a bug. HelpFormatter accepts color, but it is then seemingly overridden in _get_formatter. This should probably be respected, or we should remove it to avoid confusion.


# create the parsers action and add it to the positionals list
parsers_class = self._pop_action_class(kwargs, 'parsers')
Expand Down
22 changes: 22 additions & 0 deletions Lib/test/test_argparse.py
Original file line number Diff line number Diff line change
Expand Up @@ -7356,6 +7356,28 @@ def __init__(self, prog):
'''))


class TestColorEnvironment(TestCase):
"""Tests for color behavior with environment variable changes."""

def test_subparser_respects_no_color_environment(self):
# Cleanup to ensure environment is properly reset
self.addCleanup(os.environ.pop, 'FORCE_COLOR', None)
self.addCleanup(os.environ.pop, 'NO_COLOR', None)

# Create parser with FORCE_COLOR, capturing colored prog
os.environ['FORCE_COLOR'] = '1'
parser = argparse.ArgumentParser(prog='complex')
sub = parser.add_subparsers(dest='command')
demo_parser = sub.add_parser('demo')

# Switch to NO_COLOR environment
os.environ.pop('FORCE_COLOR', None)
os.environ['NO_COLOR'] = '1'

# Subparser help should have no color codes
help_text = demo_parser.format_help()
self.assertNotIn('\x1b[', help_text)

class TestModule(unittest.TestCase):
def test_deprecated__version__(self):
with self.assertWarnsRegex(
Expand Down
Loading