tags:

views:

141

answers:

5

I'm trying to check that a user of this code runs this program from command line using only one command line parameter (the name of the file). When I run this code like this (I'm on windows)

C:\>python program.py
Usage: program.py <file.txt>
C:\>

Right. But when I run this program using a file I want to manipulate, I get nothing printed:

C:\>python program.py file.txt

C:\>

Where's the problem, my code is here

#!/Python26/
# -*- coding: utf-8 -*-

import sys

def main(argv):
    if len(argv) < 2:
        sys.stderr.write("Usage: %s <file.txt>" % (argv[0],))
        return 1

if __name__ == "__main__":
    sys.exit(main(sys.argv))

f = open(sys.argv[1])
lines = f.readlines()
f.close()

for line in lines:
    line = line.strip()
    etc...
A: 

Here's your problem:

def main(argv):
    if len(argv) < 2:
        sys.stderr.write("Usage: %s <file.txt>" % (argv[0],))
        return 1
if __name__ == "__main__":
    sys.exit(main(sys.argv))

Your main function returns 1 if len(argv) < 2, otherwise it returns None. Nothing after if __name__ == "__main__" is evaluated.

As a minor aside, you don't need to make your string-formatting argument a tuple if it is only one item. This is equally valid, if not a little more clear:

sys.stderr.write("Usage: %s <file.txt>" % argv[0])
Mark Rushakoff
+4  A: 

You need to move the f = open... and beyond into the main function. As it stands, it never gets executed because execution ends at the sys.exit call.

[EDIT] Structuring a module this way is a common Python idiom, BTW. In this way, a file may contain class and function definitions which can be imported by another module and it can also contain code, for example, tests, that is executed only when the file is run directly as a script.

Ned Deily
+3  A: 

I guess you want:

#!/Python26/
# -*- coding: utf-8 -*-

import sys

def main(argv):
    if len(argv) < 2:
        sys.stderr.write("Usage: %s <file.txt>" % (argv[0],))
        return 1

    f = open(sys.argv[1])
    lines = f.readlines()
    f.close()

    for line in lines:
        line = line.strip()
        etc...

if __name__ == "__main__":
    sys.exit(main(sys.argv))
flokra
+1  A: 
import sys

def main(argv):
    if len(argv) < 2:
        sys.stderr.write("Usage: %s <file.txt>" % (argv[0],))
        return 1

    f = open(sys.argv[1])
    lines = f.readlines()
    f.close()

    for line in lines:
        line = line.strip()
        etc...

    return 0

if __name__ == "__main__":
    sys.exit(main(sys.argv))
John Kugelman
+2  A: 

I notice the peculiar fact that everybody's code is suggesting that your main should ignore its arguments (except for checking it has enough) and reach back right into sys.argv instead. That's truly weird and I can't imagine any case where it would be reasonable -- makes main hard to reuse and surprising if this module ever gets imported, hard to test, and all without any advantage whatsoever with respect to the obvious, plain way to do it.

So, the way I recommend you to code this is, instead:

import sys


def main(argv):

    if len(argv) < 2:
        sys.stderr.write("Usage: %s <file.txt>" % argv[0])
        return 1

    with open(argv[1]) as f:
        for line in f:
            line = line.strip()
            # etc, etc...

    return 0


if __name__ == "__main__":
    sys.exit(main(sys.argv))

You'll notice a few more suggested changes in this variant -- for example, I'm assuming you're using the current production version of Python, 2.6, so that the with statement is available (in 2.5 it needed a from __future__ import), etc, etc. But the main point is, a function should use its arguments, not reach back into a different module to grab them all over again when they've already been passed to it!-)

Alex Martelli