Python append to system arguments – bad practice?

Question:

I’m parsing system arguments in my Python project using sys.argv. At some point i had to modify the script after having written the logic that parses system args. I added a line the basically appends a string to sys.argv so the logic that parses it won’t be changed –

sys.argv.append('some string here')

Is it a bad practice to modify the system arguments after they have been created for the program ?

Asked By: Caffeine

||

Answers:

It is bad practice to modify sys.argv in Python and it’s equivalent in other languages.

In these situations I recommend a parsed_args variable which has all your parsed data from sys.argv, any default values that you would like to set, and any modifications that “middleware” would make.

Answered By: dotancohen

In my opinion in such case it would be better to first parse those arguments using argparse.

That way you can get Namespace object, that can be modified and much easier to maintain than hardcodingsys.argv.

After that you can modify Namespace any way you want

Small example:

def parse_args():
    parser = argparse.ArgumentParser(description="Description")
    parser.add_argument('--file_path')
    parsed_args = parser.parse_args()
    return parsed_args

if __name__ == '__main__':
    args = parse_args()
    print(args.file_path) # the argument passed while running script
    args.another_value = "value"
    print(args.another_value) # value added within code
    setattr(args, 'yet_another', 'value') # another way to set attributes
Answered By: Shan

Generally, I like to leave the original command-line (argv) alone and work on a copy. That way, I can always see the original. Perhaps I want to compare what the user actually typed out with what I filled in later from environment variables, default values, or whatever. That’s pretty handy while tracking down a problem.

Consider the case where you are using argparse. The particular library isn’t important and this is a good practice for just about anything. Instead of relying on a default source, such as sys.argv, always supply it.

In this incomplete example, main() takes a list that you supply. That can be from sys.argv or whatever list you like. That’s pretty handy for testing as you supply different lists to check various scenarios. This decoupling gives you quite a bit of flexibility:

def main(options):
    parsed_args = process_args(options)
    ... rest of program ...

def process_args(options):
    ...do stuff to fix up options, like enable things by env vars...
    parser = argparse.ArgumentParser(...)
    parser.add_argument(...)
    parsed_args = parser.parse_args(options) # argument instead of default
    ...fix up parsed_args for whatever you want to add...
    return parsed_args

if __name__ == "__main__":
    main(sys.argv[1:]) # remember the program name is the first item

Magic or default variables are nice for quick and dirty things, but those generally constrain you too much for larger projects where consumers might want to do things you hadn’t considered. Remember, Pythonic code likes to be explicit rather than implicit (yet, so many opportunities in core to be implicit!).

Answered By: brian d foy
Categories: questions Tags: ,
Answers are sorted by their score. The answer accepted by the question owner as the best is marked with
at the top-right corner.