1

I have a commandline script that works perfectly fine. Now I want to make my tool more intuitive.

I have:

parser.add_argument("-s",help = "'*.sam','*.fasta','*.fastq'", required=True) 

right now, python script.py -s savefile.sam works but I would like it to be python script.py > savefile.sam

parser.add_argument("->",help = "'*.sam','*.fasta','*.fastq'", required=True) 

does not work as it gives: error: unrecognized arguments: -

can I do this with argparse or should I settle for -s?

8
  • 5
    > savefile.sam looks like shell syntax. It makes your command line less intuitive as using it would require using shell syntax escapes. Commented Mar 24, 2015 at 13:03
  • I'd definitely settle for sticking to - for options, that's the convention. Commented Mar 24, 2015 at 13:03
  • …(just adding to the first comment) and since the shell syntax has priority it won't even work. Commented Mar 24, 2015 at 13:04
  • if it is possible to make it work with python script.py > savefile.sam that would be more intuitive. if I can get it to work but only with shell escapes then you are right and then I should settle for -s Commented Mar 24, 2015 at 13:05
  • 3
    > is interpreted by the shell, it does not even pass to Python. It redirects stdout into a file. As such, you just need to output to stdout, done. Commented Mar 24, 2015 at 13:05

2 Answers 2

4

> savefile.sam is shell syntax and means "send output to the file savefile.sam". Argparse won't even see this part of the command because the shell will interpret it first (assuming you issue this command from a suitable shell).

While your command does make sense, you shouldn't try to use argparse to implement it. Instead, if an -s isn't detected, simply send the script's output to stdout. You can achieve this by setting the default for -s:

parser.add_argument("-s", type=argparse.FileType("w"), help="'*.sam','*.fasta','*.fastq'", default=sys.stdout) 

This way, you can run python script.py > savefile.sam, and the following will happen:

  1. The shell will evaluate python script.py.
  2. argparse will see no additional arguments, and will use the default sys.stdout.
  3. Your script will send output to stdout.
  4. The shell will redirect the script's output from stdout to savefile.sam.

Of course, you can also send the stdout of the script into the stdin the another process using a pipe.

Note that, using FileType, it's also legal to use -s - to specify stdout. See here for details.

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

5 Comments

I will make required=False required=True and stick with the -s option. If I am correct, the argparse FileType instantly opens the file. and that is something that should not be done in my case (these files are 50GB+)
@zazga: Opening a file does not read its contents into memory :) Either way, you needn't use FileType if you don't want to – just check args.s whenever it's appropriate and decide what to do (either print to stdout or write to file).
learning every day! what is faster, writing to file or print to stdout?
It depends what you're doing. If you give the file directly to the program using -s, then your code can seek through the file (it can't do this if it's writing to stdout). This may or may not be an advantage to you. However, the example code that I provided has the advantage that you can either pass a file directly to the program or use shell redirection, depending on your preference. Either way, I don't think you'll see much difference unless your code's performance is limited by how quickly it can write its output to disk.
then I will just stick with whatever works! thanks for your input though
0

In a sense your argparse works

import argparse import sys print sys.argv parser=argparse.ArgumentParser() parser.add_argument('->') print parser.parse_args('-> test'.split()) print parser.parse_args() 

with no arguments, it just assigns None to the > attribute. Note though that you can't access this attribute as args.>. You'd have to use getattr(args,'>') (which is what argparse uses internally). Better yet, assign this argument a proper long name or dest.

1401:~/mypy$ python stack29233375.py ['stack29233375.py'] Namespace(>='test') Namespace(>=None) 

But if I give a -> test argument, the shell redirection consumes the >, as shown below:

1405:~/mypy$ python stack29233375.py -> test usage: stack29233375.py [-h] [-> >] stack29233375.py: error: unrecognized arguments: - 1408:~/mypy$ cat test ['stack29233375.py', '-'] Namespace(>='test') 

Only the - passes through in argv, and on to the parser. So it complains about unrecognized arguments. An optional positional argument could have consumed this string, resulting in no errors.

Notice that I had to look at the test file to see the rest of the output - because the shell redirected stdout to test. The error message goes to stderr, so it doesn't get redirected.

You can change the prefix char from - (or in addition to it) with an ArgumentParser parameter. But you can't use such a character alone. The flag must be prefix + char (i.e. 2 characters).

Finally, since this argument is required, do you even need a flag? Just make it a positional argument. It's quite common for scripts to take input and/or output file names as positional arguments.

1 Comment

I have been thinking about making it a positional argument and I came up with a reason why not to. But that was yesterday and today It doesn't come to mind again so I think I should just go for it! thanks for getattr(args,'>') saved me a lot of head scratching. I already used a long name

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.