2

Currently, I have a script which does the following. If I have text file with the lines:

<<Name>> is at <<Location>>.
<<Name>> is feeling <<Emotion>>.

The script will take in the this input file as a command line argument and prompt the user for the variables:

Name? Bob
Location? work
Emotion? frustrated

Note that name is only asked once. The script also takes in an output file as an argument and will place in the file the following.

Bob is at work.
Bob is feeling frustrated.

Now I am trying to extend the script so that I can input the variables from the command line (as if I already knew what it was going to ask). So the command would be like (in this case):

python script.py infile outfile Bob work frustrated

And it would generate the same output. Ideally, the extension should prompt the user for remaining variables if there are more remaining after those put in the command line. So if I run the script as:

python script.py infile outfile Bob work

The script would still prompt:

Emotion?

Excess variables in the command line would be ignored. I am pretty new to Python, so while this seems pretty simple, I haven't had success updating my current script with this add-on. Attached is the script:

import argparse
from os import path
import re

replacements = {}
pattern = '<<([^>]*)>>'

def user_replace(match):
    ## Pull from replacements dict or prompt
    placeholder = match.group(1)
    if placeholder in replacements:
        return replacements[placeholder]
    ## .setdefault(key, value) returns the value if present, else sets it then returns
    return replacements.setdefault(placeholder, raw_input('%s? ' % placeholder))

def main():
    parser = argparse.ArgumentParser()
    parser.add_argument('infile', type=argparse.FileType('r'))
    parser.add_argument('outfile', type=argparse.FileType('w'))
    args = parser.parse_args()

    matcher = re.compile(pattern)

    for line in args.infile:
        new_line = matcher.sub(user_replace, line)
        args.outfile.write(new_line)

    args.infile.close()
    args.outfile.close()

if __name__ == '__main__':
    main()

Edit: The input file example above is arbitrary. In actuality, the input file could have any number of variables, repeated any number of times.

1 Answer 1

3

OK, if you want to dynamically generate the options:

parser = argparse.ArgumentParser()
parser.add_argument('infile', type=argparse.FileType('r'))
parser.add_argument('outfile', type=argparse.FileType('w'))

required, extra = parser.parse_known_args()
infile, outfile = required.infile, required.outfile

args = re.findall(pattern, infile.read())
infile.seek(0)

parser = argparse.ArgumentParser()
for arg in args:
    parser.add_argument('--' + arg.lower())

replacements = vars(parser.parse_args(extra))

This will give you a dictionary of all the arguments. Each argument value will be a string inside a list. Then you just do

def user_replace(match):
    """Pull from replacements dict or prompt"""
    placeholder = match.group(1)
    return (replacements[placeholder][0] 
             if placeholder in replacements else 
              raw_input('%s? ' % placeholder))

Edit: I've edited the code at the top to set the arguments. This way, the arguments are optional, and you can have any number. Their names will still be name, location, and emotion in replacements.

The user can now do:

python script.py infile outfile --name Bob --emotion 'extremely frustrated'

leaving out the ones they want to be prompted for, and enclosing strings with spaces in quotes.

Edit 2: Edited the top part so it dynamically gets the args from the textfile.

Sign up to request clarification or add additional context in comments.

12 Comments

Is there any way to loop that first bit so it would work for any number of arguments? The name, location, emotion bit above was just an example. In reality, the user might have many more args.
I'm not sure what you want but I updated my answer with a guess.
My idea is that, say I add a new line to the input file: <<Name>> is now at a <<Location2>>. Then, using the same script, I could write four arguments so it would look like: "python script.py infile outfile Bob work frustrated party" The output would then be: "Bob is at work. Bob is feeling frustrated. Bob is now at a party."
You'd need a two-step solution: first use sys.argv[1] to get the infile name. Then parse the file to get the argument names. Then loop over them adding the arguments to the parser. Then parse the arguments, match the lines, and write to the output file. Editing my answer now. Please make sure to accept my answer if it helps.
argparse is very nice ... it also has a great feature called parse_known_args ... which will allow you to have backwards compatibility ... I would suggest trying to leverage this module
|

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.