1

Let's say I want to make a hashing script:

### some code here

def hashlib_based(path, htype='md5', block_size=2**16):
    hash = eval(htype)
    with open(path, 'rb') as f:
        for block in iter(lambda: f.read(block_size), ''):
            hash().update(block)
        f.close()
    return hash().hexdigest()

### some code here

As you can see, I have the opportunity to use different flags to allow me to change the hash type or the block size when I call the script from the command line (for example ./myscript.py -sha1 -b 512 some_file.ext). The thing is, I don't have any clue on how should I do this in order to keep my code as clean and readable as possible. How do I deal with sys.argv?

First of all, how do I check if the user uses the correct flags? I need to do that in order to print out a usage message. Do I make a list with all the flags, then I check if the user uses one that is in that list?

Should I do all these things inside main() or should I do place them in a different function?

Should I construct my flags with a hyphen-minus in front of them (like this: -a, -b) or without one? To check if a certain flag is present in sys.argv, do I simply do something like:

if '-v' in sys.argv:
    verbose = True

?

Because sys.argv has indexes, what is the best way to ignore the order of the flags - or in other words, should ./myscript.py -a -b be the same as ./myscript.py -b -a? While it certainly makes the job easier for the common user, is it common practice to do so?

I saw something similar but for C#. Is there a similar concept in Python? The thing is, as simple as these things are, they get out of hands quickly - for me at least. I end up doing a mess. What is your approach to this problem?

Community
  • 1
  • 1
Deneb
  • 119
  • 1
  • 2
  • 10

1 Answers1

3

For really simple use cases, such as checking the presence of one argument, you can do a check like you're showing, i.e.:

if '-v' in sys.argv: ...

which is the quick'n dirty way of checking arguments. But once your project gets a bit more serious, you definitely need to use an argument parsing library.

And there are a few ones to handle argument parsing: there is the now deprecated getopt (I won't give a link), the most common one is argparse which is included in any python distribution.

import argparse
parser = argparse.ArgumentParser()
parser.add_argument('-a', '--a-long', help='a help')
parser.add_argument('-b', '--b-long', help='b help')
args = parser.parse_args()

then you can call your script -a -b or script -b -a which will be equivalent. And for free, you've got script -h for free! :-)

Though, my preference is now over docopt, imho, which is way simpler and more elegant, for the same example:

"""
My script.

usage:
    myscript -a | --along
    myscript -b | --blong

Options:
  -a --along     a help
  -b --blong     b help
"""

from docopt import docopt
arguments = docopt(__doc__, version='myscript 1.0')
print(arguments)

HTH

Chris Johnson
  • 20,650
  • 6
  • 81
  • 80
zmo
  • 24,463
  • 4
  • 54
  • 90
  • Thanks for pointing out docopt. Turns out I was trying to deal with all these problems without knowing of the existence of argument parsers. Makes me wonder how much else is there to learn. – Deneb Feb 25 '14 at 17:15
  • And the good thing about docopt, it's that it works the same accross languages :-) – zmo Feb 25 '14 at 17:16