mirror of
				https://github.com/python/cpython.git
				synced 2025-10-30 21:21:22 +00:00 
			
		
		
		
	Issue #13107: argparse and optparse no longer raises an exception when output
a help on environment with too small COLUMNS. Based on patch by Elazar Gershuni.
This commit is contained in:
		
							parent
							
								
									32c4915b23
								
							
						
					
					
						commit
						f451112413
					
				
					 6 changed files with 103 additions and 6 deletions
				
			
		|  | @ -165,6 +165,8 @@ def __init__(self, | ||||||
|         self._prog = prog |         self._prog = prog | ||||||
|         self._indent_increment = indent_increment |         self._indent_increment = indent_increment | ||||||
|         self._max_help_position = max_help_position |         self._max_help_position = max_help_position | ||||||
|  |         self._max_help_position = min(max_help_position, | ||||||
|  |                                       max(width - 20, indent_increment * 2)) | ||||||
|         self._width = width |         self._width = width | ||||||
| 
 | 
 | ||||||
|         self._current_indent = 0 |         self._current_indent = 0 | ||||||
|  | @ -336,7 +338,7 @@ def get_lines(parts, indent, prefix=None): | ||||||
|                     else: |                     else: | ||||||
|                         line_len = len(indent) - 1 |                         line_len = len(indent) - 1 | ||||||
|                     for part in parts: |                     for part in parts: | ||||||
|                         if line_len + 1 + len(part) > text_width: |                         if line_len + 1 + len(part) > text_width and line: | ||||||
|                             lines.append(indent + ' '.join(line)) |                             lines.append(indent + ' '.join(line)) | ||||||
|                             line = [] |                             line = [] | ||||||
|                             line_len = len(indent) - 1 |                             line_len = len(indent) - 1 | ||||||
|  | @ -476,7 +478,7 @@ def _format_actions_usage(self, actions, groups): | ||||||
|     def _format_text(self, text): |     def _format_text(self, text): | ||||||
|         if '%(prog)' in text: |         if '%(prog)' in text: | ||||||
|             text = text % dict(prog=self._prog) |             text = text % dict(prog=self._prog) | ||||||
|         text_width = self._width - self._current_indent |         text_width = max(self._width - self._current_indent, 11) | ||||||
|         indent = ' ' * self._current_indent |         indent = ' ' * self._current_indent | ||||||
|         return self._fill_text(text, text_width, indent) + '\n\n' |         return self._fill_text(text, text_width, indent) + '\n\n' | ||||||
| 
 | 
 | ||||||
|  | @ -484,7 +486,7 @@ def _format_action(self, action): | ||||||
|         # determine the required width and the entry label |         # determine the required width and the entry label | ||||||
|         help_position = min(self._action_max_length + 2, |         help_position = min(self._action_max_length + 2, | ||||||
|                             self._max_help_position) |                             self._max_help_position) | ||||||
|         help_width = self._width - help_position |         help_width = max(self._width - help_position, 11) | ||||||
|         action_width = help_position - self._current_indent - 2 |         action_width = help_position - self._current_indent - 2 | ||||||
|         action_header = self._format_action_invocation(action) |         action_header = self._format_action_invocation(action) | ||||||
| 
 | 
 | ||||||
|  |  | ||||||
|  | @ -209,7 +209,6 @@ def __init__(self, | ||||||
|                  short_first): |                  short_first): | ||||||
|         self.parser = None |         self.parser = None | ||||||
|         self.indent_increment = indent_increment |         self.indent_increment = indent_increment | ||||||
|         self.help_position = self.max_help_position = max_help_position |  | ||||||
|         if width is None: |         if width is None: | ||||||
|             try: |             try: | ||||||
|                 width = int(os.environ['COLUMNS']) |                 width = int(os.environ['COLUMNS']) | ||||||
|  | @ -217,6 +216,8 @@ def __init__(self, | ||||||
|                 width = 80 |                 width = 80 | ||||||
|             width -= 2 |             width -= 2 | ||||||
|         self.width = width |         self.width = width | ||||||
|  |         self.help_position = self.max_help_position = \ | ||||||
|  |                 min(max_help_position, max(width - 20, indent_increment * 2)) | ||||||
|         self.current_indent = 0 |         self.current_indent = 0 | ||||||
|         self.level = 0 |         self.level = 0 | ||||||
|         self.help_width = None          # computed later |         self.help_width = None          # computed later | ||||||
|  | @ -261,7 +262,7 @@ def _format_text(self, text): | ||||||
|         Format a paragraph of free-form text for inclusion in the |         Format a paragraph of free-form text for inclusion in the | ||||||
|         help output at the current indentation level. |         help output at the current indentation level. | ||||||
|         """ |         """ | ||||||
|         text_width = self.width - self.current_indent |         text_width = max(self.width - self.current_indent, 11) | ||||||
|         indent = " "*self.current_indent |         indent = " "*self.current_indent | ||||||
|         return textwrap.fill(text, |         return textwrap.fill(text, | ||||||
|                              text_width, |                              text_width, | ||||||
|  | @ -342,7 +343,7 @@ def store_option_strings(self, parser): | ||||||
|         self.dedent() |         self.dedent() | ||||||
|         self.dedent() |         self.dedent() | ||||||
|         self.help_position = min(max_len + 2, self.max_help_position) |         self.help_position = min(max_len + 2, self.max_help_position) | ||||||
|         self.help_width = self.width - self.help_position |         self.help_width = max(self.width - self.help_position, 11) | ||||||
| 
 | 
 | ||||||
|     def format_option_strings(self, option): |     def format_option_strings(self, option): | ||||||
|         """Return a comma-separated list of option strings & metavariables.""" |         """Return a comma-separated list of option strings & metavariables.""" | ||||||
|  |  | ||||||
|  | @ -2973,6 +2973,60 @@ class TestHelpBiggerOptionals(HelpTestCase): | ||||||
|         0.1 |         0.1 | ||||||
|         ''' |         ''' | ||||||
| 
 | 
 | ||||||
|  | class TestShortColumns(HelpTestCase): | ||||||
|  |     '''Test extremely small number of columns. | ||||||
|  | 
 | ||||||
|  |     TestCase prevents "COLUMNS" from being too small in the tests themselves, | ||||||
|  |     but we don't want any exceptions thrown in such case. Only ugly representation. | ||||||
|  |     ''' | ||||||
|  |     def setUp(self): | ||||||
|  |         env = support.EnvironmentVarGuard() | ||||||
|  |         env.set("COLUMNS", '15') | ||||||
|  |         self.addCleanup(env.__exit__) | ||||||
|  | 
 | ||||||
|  |     parser_signature            = TestHelpBiggerOptionals.parser_signature | ||||||
|  |     argument_signatures         = TestHelpBiggerOptionals.argument_signatures | ||||||
|  |     argument_group_signatures   = TestHelpBiggerOptionals.argument_group_signatures | ||||||
|  |     usage = '''\ | ||||||
|  |         usage: PROG | ||||||
|  |                [-h] | ||||||
|  |                [-v] | ||||||
|  |                [-x] | ||||||
|  |                [--y Y] | ||||||
|  |                foo | ||||||
|  |                bar | ||||||
|  |         ''' | ||||||
|  |     help = usage + '''\ | ||||||
|  | 
 | ||||||
|  |         DESCRIPTION | ||||||
|  | 
 | ||||||
|  |         positional arguments: | ||||||
|  |           foo | ||||||
|  |             FOO HELP | ||||||
|  |           bar | ||||||
|  |             BAR HELP | ||||||
|  | 
 | ||||||
|  |         optional arguments: | ||||||
|  |           -h, --help | ||||||
|  |             show this | ||||||
|  |             help | ||||||
|  |             message and | ||||||
|  |             exit | ||||||
|  |           -v, --version | ||||||
|  |             show | ||||||
|  |             program's | ||||||
|  |             version | ||||||
|  |             number and | ||||||
|  |             exit | ||||||
|  |           -x | ||||||
|  |             X HELP | ||||||
|  |           --y Y | ||||||
|  |             Y HELP | ||||||
|  | 
 | ||||||
|  |         EPILOG | ||||||
|  |     ''' | ||||||
|  |     version                     = TestHelpBiggerOptionals.version | ||||||
|  | 
 | ||||||
| 
 | 
 | ||||||
| class TestHelpBiggerOptionalGroups(HelpTestCase): | class TestHelpBiggerOptionalGroups(HelpTestCase): | ||||||
|     """Make sure that argument help aligns when options are longer""" |     """Make sure that argument help aligns when options are longer""" | ||||||
|  |  | ||||||
|  | @ -1443,6 +1443,39 @@ def test_conflict_override_args(self): | ||||||
|   -h, --help         show this help message and exit |   -h, --help         show this help message and exit | ||||||
| """ | """ | ||||||
| 
 | 
 | ||||||
|  | _expected_very_help_short_lines = """\ | ||||||
|  | Usage: bar.py [options] | ||||||
|  | 
 | ||||||
|  | Options: | ||||||
|  |   -a APPLE | ||||||
|  |     throw | ||||||
|  |     APPLEs at | ||||||
|  |     basket | ||||||
|  |   -b NUM, --boo=NUM | ||||||
|  |     shout | ||||||
|  |     "boo!" NUM | ||||||
|  |     times (in | ||||||
|  |     order to | ||||||
|  |     frighten | ||||||
|  |     away all | ||||||
|  |     the evil | ||||||
|  |     spirits | ||||||
|  |     that cause | ||||||
|  |     trouble and | ||||||
|  |     mayhem) | ||||||
|  |   --foo=FOO | ||||||
|  |     store FOO | ||||||
|  |     in the foo | ||||||
|  |     list for | ||||||
|  |     later | ||||||
|  |     fooing | ||||||
|  |   -h, --help | ||||||
|  |     show this | ||||||
|  |     help | ||||||
|  |     message and | ||||||
|  |     exit | ||||||
|  | """ | ||||||
|  | 
 | ||||||
| class TestHelp(BaseTest): | class TestHelp(BaseTest): | ||||||
|     def setUp(self): |     def setUp(self): | ||||||
|         self.parser = self.make_parser(80) |         self.parser = self.make_parser(80) | ||||||
|  | @ -1500,6 +1533,8 @@ def test_wrap_columns(self): | ||||||
|         # we look at $COLUMNS. |         # we look at $COLUMNS. | ||||||
|         self.parser = self.make_parser(60) |         self.parser = self.make_parser(60) | ||||||
|         self.assertHelpEquals(_expected_help_short_lines) |         self.assertHelpEquals(_expected_help_short_lines) | ||||||
|  |         self.parser = self.make_parser(0) | ||||||
|  |         self.assertHelpEquals(_expected_very_help_short_lines) | ||||||
| 
 | 
 | ||||||
|     def test_help_unicode(self): |     def test_help_unicode(self): | ||||||
|         self.parser = InterceptingOptionParser(usage=SUPPRESS_USAGE) |         self.parser = InterceptingOptionParser(usage=SUPPRESS_USAGE) | ||||||
|  |  | ||||||
|  | @ -430,6 +430,7 @@ Marius Gedminas | ||||||
| Thomas Gellekum | Thomas Gellekum | ||||||
| Gabriel Genellina | Gabriel Genellina | ||||||
| Christos Georgiou | Christos Georgiou | ||||||
|  | Elazar Gershuni | ||||||
| Ben Gertzfield | Ben Gertzfield | ||||||
| Nadim Ghaznavi | Nadim Ghaznavi | ||||||
| Dinu Gherman | Dinu Gherman | ||||||
|  |  | ||||||
|  | @ -43,6 +43,10 @@ Core and Builtins | ||||||
| Library | Library | ||||||
| ------- | ------- | ||||||
| 
 | 
 | ||||||
|  | - Issue #13107: argparse and optparse no longer raises an exception when output | ||||||
|  |   a help on environment with too small COLUMNS.  Based on patch by | ||||||
|  |   Elazar Gershuni. | ||||||
|  | 
 | ||||||
| - Issue #20207: Always disable SSLv2 except when PROTOCOL_SSLv2 is explicitly | - Issue #20207: Always disable SSLv2 except when PROTOCOL_SSLv2 is explicitly | ||||||
|   asked for. |   asked for. | ||||||
| 
 | 
 | ||||||
|  |  | ||||||
		Loading…
	
	Add table
		Add a link
		
	
		Reference in a new issue
	
	 Serhiy Storchaka
						Serhiy Storchaka