I'm old -- I admit it -- and I feel that command-line applications are still very, very important. Linux, for example, is packed full of almost innumerable command-line applications. In some cases, the Linux GUI tools are specifically just wrappers around the underlying command-line applications.
For many types of high-volume data processing, command-line applications are essential.
I've seen command-line applications done very badly.
Overusing Main
When writing OO programs, it's absolutely essential that the OS interface (public static void main in Java or the if __name__ == "__main__": block in Python) does as little as possible.
A good command-line program has the underlying tasks or actions defined in some easy-to-work with class hierarchy built on the Command design pattern. The actual main program part does just a few things: gather the relevant environment variables, parse command-line options and arguments, identify the configuration files, and initiate the appropriate commands. Nothing application-specific.
When the main method does application-specific work, that application functionality is buried in a method that's particularly hard to reuse. It's important to keep the application functionality away from the OS interface. I'm finding that main programs should look something like this:
if \__name_\_ == "__main__": logging.basicConfig( stream=sys.stderr ) args= parse_args() logging.getLogger().setLevel( args.verbosity ) try: for file in args.file: with open( file, "r" ) as source: process_file( source, args ) status= 0 except Exception as e: logging.exception( e ) status= 3 logging.shutdown() sys.exit( status )
That's it. Nothing more in the top-level main program. The process_file function becomes a reusable "command" and something that can be tested independently.
This is my standard main
Chai<noreply@blogger.com>
2011-10-11 18:24:57.551000-04:00
This is my standard main.
if __name__ == '__main__' : if os.path.exists( "logging.conf" ) : logging.config.fileConfig("logging.conf") else : logging.basicConfig(level=logging.INFO) fnName = sys.argv[ 1 ] logging.info( '** function %s', fnName ) n = locals()[ fnName ]( args = sys.argv[ 2: ] ) logging.info( '** bye' )
Thank you for posting this. This is actually somet...
Kellan<noreply@blogger.com>
2011-10-06 18:08:52.652000-04:00
Thank you for posting this. This is actually something I knew I was doing wrong but I didn't quite know exactly where I was making the wrong turn. This gets me much closer to the clean code I would like to be writing in python. Any chance you have a simple script you wrote you would like to share with us, that uses this method? I would love to see an example I can read over and use to learn from.
I myself like to wrap this in a main function...
Ids<noreply@blogger.com>
2011-10-07 03:49:17.887000-04:00
Hi. I myself like to wrap this in a main function like this:
def main(argv): ... if __name__ == '__main__': main(sys.argv)
Also, the standard python fileinput module may come in handy.