You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
{{ message }}
Repository navigation
Argparse wrapping is bugged when colors are involved #142035
While working on coloring interpolated values in #141940, I realized that in some cases the wrapping is broken.
For example in this case, just a color change around the commas makes the wrapping different, because textwrap.wrap is sensitive to ANSI escape codes (those change the string length).
There are 2 ways to fix this:
have a more clever wrapping that is not sensitive to ANSI escape codes
make sure the wrapping always happens before ANSI escape codes are inserted
Looking a bit more at the code, I believe it would make more sense to add a parameter to textwrap to ignore ANSI escape codes when wrapping/filling, then leverage it in argparse.
With the recent addition of colors in the stdlib, I think it makes sense to have that option in textwrap directly. I would argue it should default to True, but I will leave that to the experts 😉.
I opened #142040 as a proof-of-concept, if this is viable for the code owners, I can add the tests, docs, etc.
EDIT: this is also an issue with multiple-code-points glyphs, textwrap is not handling them cleanly, so it can cause other wrapping issues:
>>>textwrap.wrap("Cafe\u0301 is good", width=4)
['Cafe', '́ is', 'good']
>>>textwrap.wrap("Cafe\u0301 is good", width=5)
['Café', 'is', 'good']
AFAICT there is no clean way in stdlib to get the actual rendering width of such strings, like what is done by third party libs like wcwidth.
importargparseparser=argparse.ArgumentParser(
description="A simple argument parser example.",
formatter_class=argparse.ArgumentDefaultsHelpFormatter,
)
parser.add_argument("--verbose", action="store_true", help="A l o n g d e s c r i p t i o n f o r t h e v e r b o s e f l a g t o d e m o n s t r a t e t h e f o r m a t t e r c l a s s u s e d i n t h i s a r g u m e n t p a r s e r .")
parser.add_argument("--input", type=str, default="input.txt", help="Input file path")
args=parser.parse_args()
Then:
$ COLUMNS=70 python test.py --help
usage: test.py [-h] [--verbose] [--input INPUT]
A simple argument parser example.
options:
-h, --help show this help message and exit
--verbose A l o n g d e s c r i p t i o n f o r t h e v e r b
o s e f l a g t o d e m o n s t r a t e t h e f o r
m a t t e r c l a s s u s e d i n t h i s a r g u m
e n t p a r s e r . (default:
False)
--input INPUT Input file path (default:
input.txt)
You can see the False for --verbose is not wrapping correctly.
If you modify the script to manually write the default value, so it does not have the colors introduced in #141680
importargparseparser=argparse.ArgumentParser(
description="A simple argument parser example.",
)
parser.add_argument("--verbose", action="store_true", help="A l o n g d e s c r i p t i o n f o r t h e v e r b o s e f l a g t o d e m o n s t r a t e t h e f o r m a t t e r c l a s s u s e d i n t h i s a r g u m e n t p a r s e r . (default: False)")
parser.add_argument("--input", type=str, default="input.txt", help="Input file path")
args=parser.parse_args()
Here is the text is wrapping correctly.
usage: test.py [-h] [--verbose] [--input INPUT]
A simple argument parser example.
options:
-h, --help show this help message and exit
--verbose A l o n g d e s c r i p t i o n f o r t h e v e r b
o s e f l a g t o d e m o n s t r a t e t h e f o r
m a t t e r c l a s s u s e d i n t h i s a r g u m
e n t p a r s e r . (default: False)
--input INPUT Input file path
Maybe we could assume that (default: ...) is actually added to the description text before we format the entire thing? It looks like we are not wrapping the same text.
Looking a bit more at the code, I believe it would make more sense to add a parameter to textwrap to ignore ANSI escape codes when wrapping/filling, then leverage it in argparse.
With the recent addition of colors in the stdlib, I think it makes sense to have that option in textwrap directly. I would argue it should default to True, but I will leave that to the experts 😉.
I opened #142040 as a proof-of-concept, if this is viable for the code owners, I can add the tests, docs, etc.
EDIT: this is also an issue with multiple-code-points glyphs, textwrap is not handling them cleanly, so it can cause other wrapping issues:
textwrap.wrap("Cafe\u0301 is good", width=4)
['Cafe', '́ is', 'good']
textwrap.wrap("Cafe\u0301 is good", width=5)
['Café', 'is', 'good']
AFAICT there is no clean way in stdlib to get the actual rendering width of such strings, like what is done by third party libs like wcwidth.
wcwidth in terminal is hard.....
pdm also found similar issue when color the arg when update to 3.14
info: pdm-project/pdm#3682
For the record, as I explained in the linked PR, I believe the best solution to fix this would be to add a way to customize the length function of textwrap, like what was proposed in #28136. argparse could then use this to avoid counting the escape codes when wrapping.
I would be happy to have a custom length because I needed this in Sphinx. If I have more time this week-end I'll try to ressurect that other PR and check whether we can do something with this. This is a bit unfortunate that this will only be present in 3.15+ though, considering it's kinda annoying in Python 3.14. It's very user-facing in general now that all CLIs are colored (I don't know if they are colored by default but I think so) so we should also find a way to make it work in 3.14.
FWIW, text wrapping in argparse is quite fragile. There are a couple open issues related to text wrapping already open (albeit the solution would be different than what is required here). I do agree that it'd be nice to fix this in a more holistic way though.
If textwrap ever gets such a custom length parameter, that would make having a way to know multiple-code-points glyphs width even more appealing 😉, see #56777 for prior discussion.
The proposed textwrap change adds a public API, so it isn’t suitable for backporting. I’ve opened #154634 with a narrower, argparse-only bug fix that should be backportable. The broader textwrap API proposal should be considered separately.
The proposed textwrap change adds a public API, so it isn’t suitable for backporting. I’ve opened #154634 with a narrower, argparse-only bug fix that should be backportable. The broader textwrap API proposal should be considered separately.
OK I will wait for feedback and eventually the merge of your #154634 PR before re-adapting my #152702 code to it.
I'm going to close this issue since it's specifically about a bug in argparse and the corresponding PRs have been merged. @kdeldycke Can you open a new issue to track the feature request for textwrap?
Also removing the 3.14 label since AFAICT this only repros on 3.15+.
I'm going to close this issue since it's specifically about a bug in argparse and the corresponding PRs have been merged. @kdeldycke Can you open a new issue to track the feature request for textwrap?
Just added an new issue at #154686 . This describe the situation and cross-link with my #152702 PR. The latter now removes your local patch and generalize it.
Activity
picnixz commentedon Nov 28, 2025
cc @hugovk @savannahostrowski
alexprengere commentedon Nov 28, 2025
Looking a bit more at the code, I believe it would make more sense to add a parameter to
textwrapto ignore ANSI escape codes when wrapping/filling, then leverage it inargparse.With the recent addition of colors in the stdlib, I think it makes sense to have that option in textwrap directly. I would argue it should default to
True, but I will leave that to the experts 😉.I opened #142040 as a proof-of-concept, if this is viable for the code owners, I can add the tests, docs, etc.
EDIT: this is also an issue with multiple-code-points glyphs, textwrap is not handling them cleanly, so it can cause other wrapping issues:
AFAICT there is no clean way in stdlib to get the actual rendering width of such strings, like what is done by third party libs like wcwidth.
hugovk commentedon Nov 29, 2025
Please can you share some example code that causes this?
alexprengere commentedon Nov 29, 2025
Running on the current CPython main:
Then:
You can see the
Falsefor--verboseis not wrapping correctly.If you modify the script to manually write the default value, so it does not have the colors introduced in #141680
Here is the text is wrapping correctly.
#142040 is one way to fix it.
picnixz commentedon Nov 29, 2025
Maybe we could assume that
(default: ...)is actually added to the description text before we format the entire thing? It looks like we are not wrapping the same text.yihong0618 commentedon Nov 29, 2025
wcwidth in terminal is hard.....
pdm also found similar issue when color the arg when update to 3.14
info: pdm-project/pdm#3682
alexprengere commentedon Dec 2, 2025
For the record, as I explained in the linked PR, I believe the best solution to fix this would be to add a way to customize the length function of
textwrap, like what was proposed in #28136.argparsecould then use this to avoid counting the escape codes when wrapping.picnixz commentedon Dec 2, 2025
I would be happy to have a custom length because I needed this in Sphinx. If I have more time this week-end I'll try to ressurect that other PR and check whether we can do something with this. This is a bit unfortunate that this will only be present in 3.15+ though, considering it's kinda annoying in Python 3.14. It's very user-facing in general now that all CLIs are colored (I don't know if they are colored by default but I think so) so we should also find a way to make it work in 3.14.
savannahostrowski commentedon Dec 2, 2025
FWIW, text wrapping in argparse is quite fragile. There are a couple open issues related to text wrapping already open (albeit the solution would be different than what is required here). I do agree that it'd be nice to fix this in a more holistic way though.
alexprengere commentedon Dec 2, 2025
If
textwrapever gets such a custom length parameter, that would make having a way to know multiple-code-points glyphs width even more appealing 😉, see #56777 for prior discussion.10 remaining items
pythongh-142035: Make `TextWrapper` ANSI-aware (python#152702)
savannahostrowski commentedon Jul 24, 2026
The proposed textwrap change adds a public API, so it isn’t suitable for backporting. I’ve opened #154634 with a narrower, argparse-only bug fix that should be backportable. The broader textwrap API proposal should be considered separately.
kdeldycke commentedon Jul 24, 2026
OK I will wait for feedback and eventually the merge of your #154634 PR before re-adapting my #152702 code to it.
GH-142035: Fix wrapping of colorized argparse help text (#154634)
pythonGH-142035: Fix wrapping of colorized argparse help text (python…
pythonGH-142035: Fix wrapping of colorized argparse help text (python…
pythonGH-142035: Fix wrapping of colorized argparse help text (python…
[3.15] GH-142035: Fix wrapping of colorized argparse help text (GH-15…
savannahostrowski commentedon Jul 24, 2026
I'm going to close this issue since it's specifically about a bug in argparse and the corresponding PRs have been merged. @kdeldycke Can you open a new issue to track the feature request for textwrap?
Also removing the 3.14 label since AFAICT this only repros on 3.15+.
pythongh-142035: Make `TextWrapper` ANSI-aware (python#152702)
kdeldycke commentedon Jul 25, 2026
Just added an new issue at #154686 . This describe the situation and cross-link with my #152702 PR. The latter now removes your local patch and generalize it.
pythonGH-142035: Fix wrapping of colorized argparse help text (python…