4

I'm considering how a Python file could be made to be an importable module as well as a script that is capable of accepting command line options and arguments as well as pipe data. How should this be done?

My attempt seems to work, but I want to know if my approach is how such a thing should be done (if such a thing should be done). Could there be complexities (such as when importing it) that I have not considered?

#!/usr/bin/env python

"""
usage:
    program [options]

options:
    --version        display version and exit
    --datamode       engage data mode
    --data=FILENAME  input data file [default: data.txt]
"""

import docopt
import sys

def main(options):

    print("main")

    datamode            = options["--datamode"]
    filename_input_data = options["--data"]

    if datamode:
        print("engage data mode")
        process_data(filename_input_data)

    if not sys.stdin.isatty():
        print("accepting pipe data")
        input_stream = sys.stdin
        input_stream_list = [line for line in input_stream]
        print("input stream: {data}".format(data = input_stream_list))

def process_data(filename):

    print("process data of file {filename}".format(filename = filename))

if __name__ == "__main__":
    options = docopt.docopt(__doc__)
    if options["--version"]:
        print(version)
        exit()
    main(options)
d3pd
  • 7,935
  • 24
  • 76
  • 127

1 Answers1

4

That's it, you're good.

Nothing matters[1] except the if __name__ == '__main__', as noted elsewhere

From the docs (emphasis mine):

A module’s __name__ is set equal to '__main__' when read from standard input, a script, or from an interactive prompt. A module can discover whether or not it is running in the main scope by checking its own __name__, which allows a common idiom for conditionally executing code in a module when it is run as a script or with python -m but not when it is imported

I also like how python 2's docs poetically phrase it

It is this environment in which the idiomatic “conditional script” stanza causes a script to run:

That guard guarantees that the code underneath it will only be accepted if it is the main function being called; put all your argument-grabbing code there. If there is no other top-level code except class/function declarations, it will be safe to import.


Other complications?

Yes:

  1. Multiprocessing (a new interpreter is started and things are re-imported). if __name__ == '__main__' covers that
  2. If you're used to C coding, you might be thinking you can protect your imports with ifdef's and the like. There's some analogous hacks in python, but it's not what you're looking for.

I like having a main method like C and Java - when's that coming out? Never.


But I'm paranoid! What if someone changes my main function. Stop being friends with that person. As long as you're the user, I assume this isn't an issue.


I mentioned the -m flag. That sounds great, what's that?! Here and here, but don't worry about it.


Footnotes:

[1] Well, the fact that you put your main code in a function is nice. Means things will run slightly faster

Community
  • 1
  • 1
en_Knight
  • 5,301
  • 2
  • 26
  • 46
  • Oh, thank you so much for such a clear and detailed answer! That was very helpful and covered a few ideas I had not considered. – d3pd Apr 22 '16 at 14:58