tags:

views:

53

answers:

3

I'm try to work out how to use optparse, but I've come to a problem.

My script (represented by this simplified example) takes a file, and does different things to it depending on options that are parsed to it. If no options are parsed nothing is done.

It makes sense to me that because of this, an error should be given if no options are given by the user. I can't work out how to do this.

I've read that options should be optional and not required. Does this mean I am using options in the wrong way? If so, how should I be doing it instead? I can't see any other way of going about it.

#!/usr/bin/python

from optparse import OptionParser

dict = {'name': foo, 'age': bar}

parser = OptionParser()

parser.add_option("-n", "--name", dest="name")
parser.add_option("-a", "--age", dest="age")

(options, args) = parser.parse_args()

if options.name:
    dict['name'] = options.name

if options.age:
    dict['age'] = options.age

print dict

#END
+2  A: 

The required argument to a program is usually given without a flag, i.e.:

munch <filename>

And not:

munch --name <filename>

This custom makes sure the user realizes that <filename> is mandatory and not optional. parse_args returns the options object and a list of leftover arguments - those without flags. If that list is short enough for you (i.e. no filename while you expected one), feel free to throw an error, and you could use optparse's capability to show usage, for that.

Eli Bendersky
But what if it's a boolean?Also they aren't really mandatory, it's just mandatory that at least one is given, so if I had them as arguments, then I wouldn't be able to omit any would I?Also, if I'm using arguments I'm missing out on the usefulness of optparse..
Acorn
@Acorn: if you have two flags, one of which is sufficient to run, then just check if they exist, and if neither was given, print out a useful error message to the user
Eli Bendersky
Would I have to do something like this? `any(getattr(options, option.dest) is not option.default for option in parser.option_list)`
Acorn
@Acorn, well in case of just two flags I'll go for a simpler check, for the sake of self-documentation
Eli Bendersky
A: 

the parse_args method will eat up all the options on the commandline (which is the text immediately following the script name that starts with - or -- (and contains a value if that particular option is defined to have a value). Everything left over after all the possible options have been parsed, is called "positional arguments". These can be accessed as the usual sys.argv[1:] list. So args that are not "optional" (as your requirement says), should really be positional args and not part of the "options", ie., they should not be of the form

myscript.py --someopt=mandatory

but

myscript.py --someopt --someotheropt <madatory arg1> <mandatory arg2>

Knowing this, you can easily write the correct logic for the sys.argv right after the pars_args call (e.g., throw an error if the remaining argv doesn't have the two mandatory args)

chetan
But doesn't that mean I'm missing out on all the functionality of optparse? I want the user to be able to give boolean options and strings, but none of them are required and at least one of them is required for something to happen.
Acorn
+2  A: 

I don't know if I'd call it "wrong" necessarily, but yeah, you are using options in a way that isn't intended. (I've done it too, for quick scripts) Consider making the first non-option argument be a command word that specifies what your script should do; if you want to allow multiple actions to be performed by the script, you could take multiple non-option arguments. This is the way that git (or Subversion, or any of many other version control systems) does it, for example:

git status

to check the status of files, or

git stash

to save a copy of your work in progress, or

git commit

to commit changes to the repository. The first non-option argument specifies the action to take. If you do it that way, it'll be easy to see whether no command has been provided by checking the length of args returned from parser.parse_args().

David Zaslavsky
The thing is, there is no main "thing the script should do". What my script is actually supposed to do is to edit a dictionary. One of my options at the moment causes the script to insert a random string somewhere into the dictionary. Another inserts a string specified in the command line into a certain place in the dictionary. This all seems like the kind of things that options are made for.
Acorn
You are bumping up against convention. If there is no reasonable modification with no options, would dumping the current state of dict be better than an error? Without predicates, `find(1)` still generates output - which is arguably better than nothing.
msw