jeremybanks (owner)

Revisions

gist: 181705 Download_button fork
public
Public Clone URL: git://gist.github.com/181705.git
Embed All Files: show embed
README.md #

This is a template intended for use as the starting point of simple command-line Python scripts. It wraps the main function, passing on the arguments the script was called with and using the function's return value as the exit code. If an invalid number of arguments are provided, it generates and displays a usage string from the function's argument list and docstring.

Here are some examples that should demonstrate exactly how this works.

def main():
    pass

$ ./hello.py too many arguments
usage: ./hello.py

With an argument:

def main(directory):
    pass

$ ./something.py too many arguments
usage: ./something.py directory

With a default argument:

def main(name, age=None):
    pass

$ ./add_member.py # no arguments
usage: ./add_member.py name [age]

With only default arguments:

def main(subject="all", verbosity=0):
    pass

$ ./help.py # no arguments
usage: ./help.py [subject [verbosity]]

With a docstring and capturing extra arguments:

def main(no, more, contrived=None, *examples):
    """I promise."""
    pass

$ ./example.py yes
usage: ./example.py no more [contrived [examples...]]

I promise.

If so inclined anyone may use this for any purpose so long as I'm not responsible if something somehow goes wrong. Letting me know that you're using it would be appreciated, but isn't required.

Author: Jeremy Banks <jeremy@jeremybanks.ca>

template.py #
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
#!/usr/bin/env python3.1
import sys
 
def main():
    print("Hello world.")
 
def __main(function, path, user_args):
    """Wraps a main function to display a usage message when necessary."""
    
    co = function.__code__
    
    num_args = co.co_argcount
 
    if function.__defaults__ is not None:
        min_args = num_args - len(function.__defaults__)
    else:
        min_args = num_args
    
    if co.co_flags & 0x04: # function captures extra arguments with *
        max_args = None
    else:
        max_args = num_args
    
    if min_args <= len(user_args) and (max_args is None or
                                       max_args >= len(user_args)):
        return(function(*user_args))
 
    if max_args == 0:
        sys.stderr.write("Usage: {path}\n".format(path=path))
    else:
        arg_list = list()
        optionals = 0
        
        for index in range(num_args):
            if index < min_args:
                arg_list.append(co.co_varnames[index])
            else:
                arg_list.append("[" + co.co_varnames[index])
                optionals += 1
                
        if max_args is None:
            arg_list.append("[" + co.co_varnames[num_args] + "...")
            optionals += 1
            
        sys.stderr.write("Usage: {path} {args}{optional_closes}\n".format
                         (path=path,
                          args=" ".join(arg_list),
                          optional_closes="]" * optionals))
    if function.__doc__:
        sys.stderr.write("\n")
        sys.stderr.write(function.__doc__)
        sys.stderr.write("\n")
    
    return(1)
 
if __name__ == "__main__":
    sys.exit(__main(main, sys.argv[0], sys.argv[1:]))