2

I'm working on a Python 3X program which requires at least one command line argument.

Currently, I'm handling arguments using the argparse library, which handles insufficient arguments by displaying a help text, and killing the program. This works great when the program is run from the command line.

However, if the user clicks on the .py or .pyc file itself, there is no opportunity for the user to read the help text, or enter arguments - the program runs and finishes almost instantly. I'd like to find out when my program is run by double-clicking a file so I can run an interactive UI in that case.

In other words, I'd like to have the arguments fed in interactively if the program is clicked on, but to keep the standard behavior of argparse if the program is run on the command line i.e., have the user specify them all at once e.g. python myprogram.py argument1.

In order to do this, I need to know if the program was run from the command line or by double-clicking a file. I know that python does have a facility somewhat like this for checking if a module is imported or run as a script if __name__ == "__main__":.

Minimal Example:

#test.py
import argparse
parser = argparse.ArgumentParser()
parser.add_argument("inputpath", help="path to plaintext input file")
args = parser.parse_args()
print(args.inputpath)

Running on the command line gives us nice error handling if a user doesn't enter appropriate arguments:

user@MyPC:path/to/file$ python test.py
usage: test.py [-h] inputpathtest.py: error: the following arguments are 
required: inputpath

However, trying to doubleclick on the file just runs it and exits immediately. There is no time to enter arguments or read the error message.


Possible solutions:

One way to do this would be an optional "interactive" argument, which if not present, would prompt the user manually. Defaulting to interactivity on would allow the user to enter inputs if the script was run by doubleclick. However, it would still allow the command line user to use the interactivity option to turn this behavior off. The downside is that the user must enter an option to get "normal" behavior (i.e. interactivity turned off).

Another way I'm investigating is that when run by doubleclick, it appears sys.argv = ["pathtofile", '', '', '', '', '', '', '', '']. This should be a unique way to tell if the script is run by doubleclick or from a terminal.

This works at least for me on Windows 10:

if sys.argv[1:]==['']*8:
    print("Running from a doubleclick")
else:
    print("running from a terminal")

So just insert your commandline and interactive UIs instead of print statements and voila.

I'm keeping this open in case anyone has a more elegant answer.

Evan Rosica
  • 1,182
  • 1
  • 12
  • 22

1 Answers1

1

You can use the length of sys.argv to get as many arguments as you want after the ones provided from the command line:

import sys
desired_args = 4  # number of arguments you want
arguments = sys.argv[1:] . # [1:] because the first one is your script
for i in range(desired_args-len(arguments)):
    arguments.append(input("Enter another argument: "))

Or, if you want to let the user input arguments until they enter a blank line, replace the for loop with:

while True:
    arg = input("Enter another argument")
    if arg == '':
        break
    else:
        arguments.append(arg)
MoxieBall
  • 1,907
  • 7
  • 23
  • 1
    Good solutions. However, I think the question was unclear. I'm looking for a way to have the arguments fed in interactively if the program is clicked on, but to keep the standard behavior of argparse if the program is run on the command line. The standard behavior of argparse is, in the case that insufficient arguments are entered to display a help text and prompt again – Evan Rosica Jun 29 '18 at 17:26
  • @EvanRosica Essentially the same thing happens if you run it from the command line or by clicking on it -- when you click on it, Windows just knows to run `yourscript.py` with `python.exe` because it has a `.py` extension. I recommend creating a script that just prints `sys.argv`, running it by clicking on it, and running it from the command line, and using that information along with this answer to get your desired functionality. – MoxieBall Jun 29 '18 at 17:30
  • Hmm just noticed something very interesting. If I run this program by double clicking it says len(sys.argv)=9, but if I run it from the command line it says len(sys.argv)=1. Program follows: `import sys print(len(sys.argv)) a=input('keepalive')` – Evan Rosica Jun 29 '18 at 18:34
  • running the script via doubleclick gives sys.argv = ["pathtofile", '', '', '', '', '', '', '', '']. I wonder why... – Evan Rosica Jun 29 '18 at 18:44
  • 1
    @EvanRosica that's actually... really neat, didn't expect that. I guess you could just check for the number of arguments that aren't empty strings – MoxieBall Jun 29 '18 at 18:55