Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Display help message in multi-line format? #555

Open
tashrifbillah opened this issue Jul 23, 2021 · 8 comments
Open

Display help message in multi-line format? #555

tashrifbillah opened this issue Jul 23, 2021 · 8 comments

Comments

@tashrifbillah
Copy link

Way to reproduce

python multi_line.py -h

multi_line.py
#!/usr/bin/env python

from plumbum import cli

class TopupEddyEpi(cli.Application):
    '''Epi and eddy correction using topup and eddy_openmp/cuda commands in fsl
    For more info, see:
        https://fsl.fmrib.ox.ac.uk/fsl/fslwiki/eddy/UsersGuide
        https://fsl.fmrib.ox.ac.uk/fsl/fslwiki/topup/TopupUsersGuide
    You can also view the help message:
        `eddy_openmp` or `eddy_cuda`
        `topup`
    '''

    dwi_file= cli.SwitchAttr(
        ['--imain'],
        help='''--imain primary4D,secondary4D/3D
                primary: one 4D volume input, should be PA;
                secondary: another 3D/4D volume input, should be AP, which is opposite of primary 4D volume''',
        mandatory=True)


    def main():
        pass

if __name__== '__main__':
    TopupEddyEpi.run()

Expected

As I wrote in the program:

--imain VALUE:str    primary4D,secondary4D/3D
                     primary: one 4D volume input, should be PA;
                     secondary: another 3D/4D volume input, should be AP, which is opposite of primary 4D volume

Actual

Spanning across my terminal in an untidy way:

    --imain VALUE:str      --imain primary4D,secondary4D/3D primary: one 4D volume input, should be PA; secondary: another 3D/4D
                           volume input, should be AP, which is opposite of primary 4D volume; required

Is the expected at all possible?

@ink-splatters
Copy link
Contributor

According to the code which handles it, it seems it does not assume multiline help messages:

for switch_info, prefix, color in switchs(by_groups, True):
help = switch_info.help # @ReservedAssignment
if switch_info.list:
help += T_("; may be given multiple times")
if switch_info.mandatory:
help += T_("; required")
if switch_info.requires:
help += T_("; requires {0}").format(
", ".join(
(("-" if len(switch) == 1 else "--") + switch)
for switch in switch_info.requires
)
)
if switch_info.excludes:
help += T_("; excludes {0}").format(
", ".join(
(("-" if len(switch) == 1 else "--") + switch)
for switch in switch_info.excludes
)
)
msg = indentation.join(
wrapper.wrap(" ".join(l.strip() for l in help.splitlines()))
)

@henryiii
Copy link
Collaborator

Probably could be fixed, PR welcome.

@ink-splatters
Copy link
Contributor

@henryiii @tashrifbillah here's what I've come up with. I could do the PR but ETA is unspecified (otherwise let someone go ahead)


first, let's split the text by line breaks:

>>> text = "This is first paragraph, of average length.\n" \
...        "And this is the second one.\n" \
...        "That's the longest paragraph, you can see it yourself."
>>> stext = text.splitlines()
>>> stext
['This is first paragraph, of average length.', 'And this is the second one.', "That's the longest paragraph, you can see it yourself."]

now, wrap the previous stage' result using TextWrapper (with width=15 for demo purpose):

>>> stext=text.splitlines()
>>> list(map(TextWrapper(width=15).wrap, stext))
[['This is first', 'paragraph, of', 'average length.'], ['And this is the', 'second one.'], ["That's the", 'longest', 'paragraph, you', 'can see it', 'yourself.']]

finally, lets create wrap() function which will also flatten the list and join it using specified indent:

>>> from textwrap import TextWrapper
>>> from typing import List
>>> 
>>> 
>>> def wrap(text: str, max_width: int, indent_size: int = 4):
...     indent=' ' * indent_size
...     return '\n'.join([
...         line for lst in map(
...             TextWrapper(width=max_width,  
...                         initial_indent=indent,
...                         subsequent_indent=indent).wrap,
...             text.splitlines()
...         )
...         for line in lst
...     ])
... 
>>> 
>>> text = """
... This is first paragraph, of average length.\n
... And this is the second one.\n
... That's the longest paragraph, you can see it yourself.
... """
>>> 
>>> print(wrap(text, max_width=15), '\n')
    This is
    first
    paragraph,
    of average
    length.
    And this is
    the second
    one.
    That's the
    longest
    paragraph,
    you can see
    it
    yourself. 

>>> print(wrap(text, max_width=80, indent_size=8))
        This is first paragraph, of average length.
        And this is the second one.
        That's the longest paragraph, you can see it yourself.

@tashrifbillah
Copy link
Author

Hi, I shall try to do it myself. I have marked the email notification.

@tashrifbillah
Copy link
Author

tashrifbillah commented Dec 31, 2021

Hi @ink-splatters , sorry about the delay--the idea that you came up with, should it be inserted before line 931?

@tashrifbillah
Copy link
Author

tashrifbillah commented Dec 31, 2021

I inserted this block:

            # break and join multi-line help message
            max_width= 200
            indent_size= 8
            indent=' ' * indent_size
            help= '\n'.join([
                line for lst in map(
                    TextWrapper(width=max_width,
                                initial_indent=indent,
                                subsequent_indent=indent).wrap,
                    help.splitlines()
                )
                for line in lst
            ])

after line 931 but no effect. I believe this block joins them back together :(

msg = indentation.join(
wrapper.wrap(" ".join(ln.strip() for ln in help.splitlines()))
)

@tashrifbillah
Copy link
Author

Replacing the latter block with msg= help gets me the following:

Switches:
    --imain VALUE:str              --imain primary4D,secondary4D/3D
        primary: one 4D volume input, should be PA;
        secondary: another 3D/4D volume input, should be AP, which is opposite of primary 4D volume; required

I see that the second and third lines of my help message do not follow the indent of first line but it is some progress :) Please let me know how we can improve upon it.

tashrifbillah added a commit to tashrifbillah/plumbum that referenced this issue Dec 31, 2021
@ink-splatters
Copy link
Contributor

Hi @ink-splatters , sorry about the delay--the idea that you came up with, should it be inserted before line 931?

Hi @tashrifbillah, I was completely incapable to answer on time, sorry for that, as well as contributing more, by personal reasons. It will likely change, probably soon.

Thanks a lot

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests

3 participants