Search code examples
pythonargparsegetopts

How do I get argparse to show one help page for a python script with two names?


My current working python script uses getopts, as it is a rewrite of a perl script that used Getopt::Long. I'd like to migrate to argparse, but failed so far as the options do not fit nicely into the way argparse works.

I then had the bright idea [?], what if I have the script have 2 names, e.g.: foo-ssh & foo-rsync

And symlink foo-rsync to foo-ssh (over 90% of the script is foo-ssh, the rsync option is an add-on that does not fit nicely as an option), then have the code in the script only handle the options for the specific name it's running under.

I know about subparsers, but that doesn't look exactly like what I'm looking for and yielded an even uglier and confusing help output than what I started with.

If I try and stick as close as I can to the getopts way with argparse I get this:

usage: foo-ssh [-h] [--version] [--debug] [--forward-agent HOST:PORT]
                [--ipv4] [--knock HOST:PORT]
                [--libvirt-vnc HOST:PORT | --rsync]
                [USER@]HOST

Which is wrong, it's right, but wrong.

From the perl script help page:

Usage: foo-ssh <options> [USER@]HOST
       foo-ssh <options> --knock HOST:PORT [USER@]HOST
       foo-ssh <options> --libvirt-vnc HOST:PORT [USER@]HOST
       foo-ssh <options> --rsync -- <rsync options> [USER@]HOST:/path/file.name /tmp

What I would like:

usage: foo-ssh [-h] [--version] [--debug] [--forward-agent HOST:PORT]
                [--ipv4] [--knock HOST:PORT] 
                [--libvirt-vnc HOST:PORT]
                [USER@]HOST
       foo-rsync [OPTION...] [USER@]HOST:SRC... [DEST]
       foo-rsync [OPTION...] SRC... [USER@]HOST:DEST

First off, am I beating a dead horse here? The whole exercise is pointless if I land up with the same amount of code or more to replace what is already working.

Secondly, is this possible and what am I looking for to achieve it?

Footnote: Perl code example not included as it's written like a hippo marking it's territory with it's own poop (i.e.: all over the place), one of the reasons for the rewrite.


Solution

  • Check the value of sys.argv[0] to see what name your script is called under, then construct an appropriate ArgumentParser in response.

    if sys.argv[0].endswith("foo-ssh"):
        parser = ArgumentParser(epilog="For rsync, run foo-rsync")
        parser.add_argument(...)
        ...
    elif sys.argv[0].endswith("foo-rsync"):
        parser = ArgumentParser(epilog="For ssh, run foo-ssh")
        parser.add_argument(...)
    else:
        sys.exit("Must be invoked as foo-ssh or foo-rsync, not {}".format(sys.argv[0]))
    
    args = parser.parse_args()
    

    (Put the reference to the other script in description if the epilog would fall "below the fold".)