|
87 | 87 |
|
88 | 88 |
|
89 | 89 | import os as _os |
90 | | -import re as _re |
91 | 90 | import sys as _sys |
92 | | -from gettext import gettext as _ |
93 | | -from gettext import ngettext |
94 | 91 |
|
95 | 92 | lazy import _colorize |
| 93 | +lazy import copy |
| 94 | +lazy import difflib |
| 95 | +lazy import re as _re |
| 96 | +lazy import shutil |
| 97 | +lazy import textwrap |
| 98 | +lazy import warnings |
| 99 | +lazy from gettext import gettext as _ |
| 100 | +lazy from gettext import ngettext |
96 | 101 |
|
97 | 102 | SUPPRESS = '==SUPPRESS==' |
98 | 103 |
|
@@ -143,10 +148,8 @@ def _copy_items(items): |
143 | 148 | return [] |
144 | 149 | # The copy module is used only in the 'append' and 'append_const' |
145 | 150 | # actions, and it is needed only when the default value isn't a list. |
146 | | - # Delay its import for speeding up the common case. |
147 | 151 | if type(items) is list: |
148 | 152 | return items[:] |
149 | | - import copy |
150 | 153 | return copy.copy(items) |
151 | 154 |
|
152 | 155 |
|
@@ -186,7 +189,6 @@ def __init__( |
186 | 189 | ): |
187 | 190 | # default setting for width |
188 | 191 | if width is None: |
189 | | - import shutil |
190 | 192 | width = shutil.get_terminal_size().columns |
191 | 193 | width -= 2 |
192 | 194 |
|
@@ -773,14 +775,38 @@ def _iter_indented_subactions(self, action): |
773 | 775 |
|
774 | 776 | def _split_lines(self, text, width): |
775 | 777 | text = self._whitespace_matcher.sub(' ', text).strip() |
776 | | - # The textwrap module is used only for formatting help. |
777 | | - # Delay its import for speeding up the common usage of argparse. |
778 | | - import textwrap |
779 | | - return textwrap.wrap(text, width) |
| 778 | + decolored = self._decolor(text) |
| 779 | + if decolored == text: |
| 780 | + return textwrap.wrap(text, width) |
| 781 | + |
| 782 | + # gh-142035: colors inflate textwrap's length counts, so wrap |
| 783 | + # the decolored text and re-apply colors per word; if textwrap |
| 784 | + # split a word, keep the plain lines (colors can't be mapped). |
| 785 | + plain = self._whitespace_matcher.sub(' ', decolored).strip() |
| 786 | + if not plain: |
| 787 | + # nothing visible to wrap (e.g. an empty interpolated value) |
| 788 | + return [text] |
| 789 | + plain_lines = textwrap.wrap(plain, width) |
| 790 | + plain_words = plain.split() |
| 791 | + colored_words = text.split() |
| 792 | + # Drop escape-only tokens (e.g. an empty interpolated value). |
| 793 | + if len(colored_words) != len(plain_words): |
| 794 | + colored_words = [ |
| 795 | + word for word in colored_words if self._decolor(word) |
| 796 | + ] |
| 797 | + colored_lines = [] |
| 798 | + start = 0 |
| 799 | + for plain_line in plain_lines: |
| 800 | + plain_line_words = plain_line.split() |
| 801 | + end = start + len(plain_line_words) |
| 802 | + if plain_words[start:end] != plain_line_words: |
| 803 | + return plain_lines |
| 804 | + colored_lines.append(' '.join(colored_words[start:end])) |
| 805 | + start = end |
| 806 | + return colored_lines |
780 | 807 |
|
781 | 808 | def _fill_text(self, text, width, indent): |
782 | 809 | text = self._whitespace_matcher.sub(' ', text).strip() |
783 | | - import textwrap |
784 | 810 | return textwrap.fill(text, width, |
785 | 811 | initial_indent=indent, |
786 | 812 | subsequent_indent=indent) |
@@ -1458,7 +1484,6 @@ class FileType(object): |
1458 | 1484 | """ |
1459 | 1485 |
|
1460 | 1486 | def __init__(self, mode='r', bufsize=-1, encoding=None, errors=None): |
1461 | | - import warnings |
1462 | 1487 | warnings.warn( |
1463 | 1488 | "FileType is deprecated. Simply open files after parsing arguments.", |
1464 | 1489 | category=PendingDeprecationWarning, |
@@ -1865,7 +1890,6 @@ class _ArgumentGroup(_ActionsContainer): |
1865 | 1890 |
|
1866 | 1891 | def __init__(self, container, title=None, description=None, **kwargs): |
1867 | 1892 | if 'prefix_chars' in kwargs: |
1868 | | - import warnings |
1869 | 1893 | depr_msg = ( |
1870 | 1894 | "The use of the undocumented 'prefix_chars' parameter in " |
1871 | 1895 | "ArgumentParser.add_argument_group() is deprecated." |
@@ -2796,7 +2820,6 @@ def _check_value(self, action, value): |
2796 | 2820 |
|
2797 | 2821 | if self.suggest_on_error and isinstance(value, str): |
2798 | 2822 | if all(isinstance(choice, str) for choice in action.choices): |
2799 | | - import difflib |
2800 | 2823 | suggestions = difflib.get_close_matches(value, action.choices, 1) |
2801 | 2824 | if suggestions: |
2802 | 2825 | args['closest'] = suggestions[0] |
@@ -2936,8 +2959,6 @@ def _warning(self, message): |
2936 | 2959 |
|
2937 | 2960 | def __getattr__(name): |
2938 | 2961 | if name == "__version__": |
2939 | | - from warnings import _deprecated |
2940 | | - |
2941 | | - _deprecated("__version__", remove=(3, 20)) |
| 2962 | + warnings._deprecated("__version__", remove=(3, 20)) |
2942 | 2963 | return "1.1" # Do not change |
2943 | 2964 | raise AttributeError(f"module {__name__!r} has no attribute {name!r}") |
0 commit comments