max_help_position is not works in python argparse library

2024/9/25 18:19:59

Hi colleagues I have the code (max_help_position is 2000):

formatter_class=lambda prog: argparse.HelpFormatter(prog, max_help_position=2000)
parser = argparse.ArgumentParser(formatter_class=formatter_class)subparsers = parser.add_subparsers(title="Commands", metavar="<command>")cmd_parser = subparsers.add_parser('long_long_long_long_long_long_long',help='- jksljdalkjda',formatter_class=formatter_class)args = parser.parse_args(['-h'])
print args

we have

optional arguments:-h, --help                          show this help message and exitCommands:<command>long_long_long_long_long_long_long- jksljdalkjdasmall                             - descr

instead

optional arguments:-h, --help  show this help message and exitCommands:<command>long_long_long_long_long_long_long - jksljdalkjdasmall                              - descr

Do you know simply way how to fix this?

The code:

class MyFormatter(argparse.HelpFormatter):def __init__(self, prog):super(MyFormatter, self).__init__(prog, max_help_position=2000)self._max_help_position = 2000self._action_max_length += 4formatter_class = MyFormatter
parser = argparse.ArgumentParser(formatter_class=formatter_class)

got same result.

The code (with width=2000)

formatter_class = lambda prog: argparse.HelpFormatter(prog,max_help_position=2000, width=2000)

got same result.

Thank you.

P.S. Also some additional small issue: this is odd spaces in "optional arguments". Do you know how to separate "Commands" and "optional arguments" for do not have spaces in "optional arguments" and have spaces in "Commands" since these are different essences?

Answer

You need to increase the width as well

try:

formatter_class=lambda prog: argparse.HelpFormatter(prog,max_help_position=100, width=200)

As my earlier thoughts (below) show, the formatting considers overall width as well as the max_position value.


(earlier)

In my limited testing, your formatter subclass seems to work. But I haven't pushed the limits.

You many need to dig more into the Formatter code.

For example there is a format_action method that actually uses the max_width

def _format_action(self, action):# determine the required width and the entry labelhelp_position = min(self._action_max_length + 2,self._max_help_position)help_width = self._width - help_positionaction_width = help_position - self._current_indent - 2...

Notice that it interacts with the width. Then it goes on to actually format the help lines and perform wrapping. So the actual implementation is not simple.

I'm not following your question about spaces. format_help commands the formatter to format a number of sections (including argument groups). The sections (usually) end with a couple of line feeds. Upon assembling them the formatter removes 'unnecessary' line feeds, leaving one space between groups. The subparser doesn't fit other categories, so I'd have to study the code to see exactly how it is handled.


Your lambda definition works as well. I haven't seen it before, and I don't think it's what the developers intended, but Python that doesn't matter - if it works.

Playing around with values and strings, I see that max_position up to about 56 works. Then it sort of sticks. But if I also change width (default is from CONSOLE), I can increase max_position further.


I was testing this with a long parser argument. Adding

parser.add_argument('-l','--long','--longlonglonglong', help='help after long option strings')

produces:

usage: issue25297.py [-h] [-l LONG] <command> ...optional arguments:-h, --help                                     show this help message andexit-l LONG, --long LONG, --longlonglonglong LONG  help after long optionstringsCommands:<command>long_long_long_long_long_long_long           - jksljdalkjda

So max_help_position does work in regular parser formatting. But for some reason, when only the subparser names are long, it does not. That section requires some special formatting. It is indented, and the subparser names are not real actions (arguments) but rather choices the subparsers argument. I'll have study it in more detail.

The subparser name string is indented 2 extra characters (compared to other arguments). The code that collects self._action_max_length does not take this into account. Hence if the subparser name is the longest string, this max_length will end up 2 spaces short. Compare actual v desired:

long_long_long_long_long_long_long- jksljdalkjda
long_long_long_long_long_long_long  - jksljdalkjda

(Formatting is done in 2 steps; once to calculate values like this _action_max_length, and a 2nd time to produce the actual output).

Subparsers are formatted with a recursive call to _format_action, so I'm not optimistic about an easy fix.


Corrected formatter

Here's a patched Formatter that correctly accounts for the indenting of subactions (sub parsers). When an argument (action) is added to the Formatter, this function figures how wide its invocation strings are, and adjusts self._max_action_length. This is used latter to indent the help strings.

class MyFormatter(argparse.HelpFormatter):"""Corrected _max_action_length for the indenting of subactions"""def add_argument(self, action):if action.help is not argparse.SUPPRESS:# find all invocationsget_invocation = self._format_action_invocationinvocations = [get_invocation(action)]current_indent = self._current_indentfor subaction in self._iter_indented_subactions(action):# compensate for the indent that will be addedindent_chg = self._current_indent - current_indentadded_indent = 'x'*indent_chginvocations.append(added_indent+get_invocation(subaction))# print('inv', invocations)# update the maximum item lengthinvocation_length = max([len(s) for s in invocations])action_length = invocation_length + self._current_indentself._action_max_length = max(self._action_max_length,action_length)# add the item to the listself._add_item(self._format_action, [action])

An example of its use (without going real wide):

# call class with alternate parameters
formatter_class=lambda prog: MyFormatter(prog, max_help_position=40,width=100)parser = argparse.ArgumentParser(formatter_class=formatter_class)parser.add_argument('-l','--long', help='help after long option strings')subparsers = parser.add_subparsers(title="Commands", metavar="<command>")cmd_parser = subparsers.add_parser('long_long_cmd',help='longish command',formatter_class=formatter_class,aliases=['long', 'long_cmd'])# newer arpgarse take aliases
sht_parser = subparsers.add_parser('short', help = 'short cmd')args = parser.parse_args(['-h'])

which displays:

usage: issue25297.py [-h] [-l LONG] <command> ...optional arguments:-h, --help                        show this help message and exit-l LONG, --long LONG              help after long option stringsCommands:<command>long_long_cmd (long, long_cmd)  longish commandshort                           short cmd
https://en.xdnf.cn/q/71546.html

Related Q&A

Python - How to parse argv on the command line using stdin/stdout?

Im new to programming. I looked at tutorials for this, but Im just getting more confused. But what Im trying to do is use stdin and stdout to take in data, pass it through arguments and print out outpu…

Bad Request from Yelp API

Inspired by this Yelp tutorial, I created a script to search for all gyms in a given city. I tweaked the script with these updates in order to return ALL gyms, not just the first 20. You can find the g…

Python: test empty set intersection without creation of new set

I often find myself wanting to test the intersection of two sets without using the result of the intersections.set1 = set([1,2]) set2 = set([2,3]) if(set1 & set2):print("Non-empty intersection…

object has no attribute show

I have installed wxpython successfully which i verified by import wxBut when I write a code import wx class gui(wx.Frame):def __init__(self,parent,id):wx.Frame.__init__(self, parent,id,Visualisation fo…

How Does Calling Work In Python? [duplicate]

This question already has answers here:Does Python make a copy of objects on assignment?(5 answers)How do I pass a variable by reference?(40 answers)Why can a function modify some arguments as percei…

Python: Sklearn.linear_model.LinearRegression working weird

I am trying to do multiple variables linear regression. But I find that the sklearn.linear_model working very weird. Heres my code:import numpy as np from sklearn import linear_modelb = np.array([3,5,7…

Implementation of Gaussian Process Regression in Python y(n_samples, n_targets)

I am working on some price data with x = day1, day2, day3,...etc. on day1, I have lets say 15 price points(y), day2, I have 30 price points(y2), and so on.When I read the documentation of Gaussian Proc…

Converting a list of points to an SVG cubic piecewise Bezier curve

I have a list of points and want to connect them as smoothly as possible. I have a function that I evaluate to get these points. I could simply use more sampling points but that would only increase the…

Python Class Inheritance AttributeError - why? how to fix?

Similar questions on SO include: this one and this. Ive also read through all the online documentation I can find, but Im still quite confused. Id be grateful for your help.I want to use the Wand class…

Is it possible to display pandas styles in the IPython console?

Is it possible to display pandas styles in an iPython console? The following code in a Jupyter notebookimport pandas as pd import numpy as npnp.random.seed(24) df = pd.DataFrame({A: np.linspace(1, 10,…