I currently have a python file that utilizes sys.argv[1] to accept a string at the command line. It then performs operations on that string and then returns the modified string to the command line.
I would like to implement a batch mode option in which I can provide a file of strings (one per line, fwiw) and have it return to the command line so that I can redirect the output doing something like
$ python script.py -someflag file.txt > modified.txt
while still retaining the current capabilities.
I am only running 2.6, so argparse is not an option. The tutorials I have seen either use argparse, getopt, or delve into examples that are too complex/don't apply.
What is the best way to check the input and act appropriately?
argparse is still an option, it's just not built into 2.6. You can still install it like any 3rd party package (for example, using easy_install argparse).
An example of code for this would be:
import sys
import argparse
p = argparse.ArgumentParser(description="script.py")
p.add_argument("-s", dest="string")
p.add_argument("-f", dest="infile")
args = p.parse_args()
if args.infile == None and args.string == None:
print "Must be given either a string or a file"
sys.exit(1)
if args.infile != None and args.string != None:
print "Must be given either a string or a file, not both"
sys.exit(1)
if args.infile:
# process the input file one string at a time
if args.string:
# process the single string
See my answer here: What's the best way to grab/parse command line arguments passed to a Python script?
As a shortcut, here's some sample code:
import optparse
parser = optparse.OptionParser()
parser.add_option('-q', '--query',
action="store", dest="query",
help="query string", default="spam")
options, args = parser.parse_args()
print 'Query string:', options.query
Related
I am trying to pass BioPython sequences to Ilya Stepanov's implementation of Ukkonen's suffix tree algorithm in iPython's notebook environment. I am stumbling on the argparse component.
I have never had to deal directly with argparse before. How can I use this without rewriting main()?
By the by, this writeup of Ukkonen's algorithm is fantastic.
An alternative to use argparse in Ipython notebooks is passing a string to:
args = parser.parse_args()
(line 303 from the git repo you referenced.)
Would be something like:
parser = argparse.ArgumentParser(
description='Searching longest common substring. '
'Uses Ukkonen\'s suffix tree algorithm and generalized suffix tree. '
'Written by Ilya Stepanov (c) 2013')
parser.add_argument(
'strings',
metavar='STRING',
nargs='*',
help='String for searching',
)
parser.add_argument(
'-f',
'--file',
help='Path for input file. First line should contain number of lines to search in'
)
and
args = parser.parse_args("AAA --file /path/to/sequences.txt".split())
Edit: It works
Using args = parser.parse_args(args=[]) would solve execution problem.
or you can declare it as class format.
class Args:
data = './data/penn'
model = 'LSTM'
emsize = 200
nhid = 200
args=Args()
I've had a similar problem before, but using optparse instead of argparse.
You don't need to change anything in the original script, just assign a new list to sys.argv like so:
if __name__ == "__main__":
from Bio import SeqIO
path = '/path/to/sequences.txt'
sequences = [str(record.seq) for record in SeqIO.parse(path, 'fasta')]
sys.argv = ['-f'] + sequences
main()
If all arguments have a default value, then adding this to the top of the notebook should be enough:
import sys
sys.argv = ['']
(otherwise, just add necessary arguments instead of the empty string)
I ended up using BioPython to extract the sequences and then editing Ilya Steanov's implementation to remove the argparse methods.
import imp
seqs = []
lcsm = imp.load_source('lcsm', '/path/to/ukkonen.py')
for record in SeqIO.parse('/path/to/sequences.txt', 'fasta'):
seqs.append(record)
lcsm.main(seqs)
For the algorithm, I had main() take one argument, his strings variable, but this sends the algorithm a list of special BioPython Sequence objects, which the re module doesn't like. So I had to extract the sequence string
suffix_tree.append_string(s)
to
suffix_tree.append_string(str(s.seq))
which seems kind of brittle, but that's all I've got for now.
I face a similar problem in invoking argsparse, the string '-f' was causing this problem. Just removing that from sys.srgv does the trick.
import sys
if __name__ == '__main__':
if '-f' in sys.argv:
sys.argv.remove('-f')
main()
Clean sys.argv
import sys; sys.argv=['']; del sys
https://github.com/spyder-ide/spyder/issues/3883#issuecomment-269131039
Here is my code which works well and I won't worry about the environment changed.
import sys
temp_argv = sys.argv
try:
sys.argv = ['']
print(sys.argv)
args = argparse.parser_args()
finally:
sys.argv = temp_argv
print(sys.argv)
Suppose you have this small code in python:
import argparse
parser = argparse.ArgumentParser()
parser.add_argument("-v", "--verbose", help="increase output verbosity",
action="store_true")
parser.add_argument("-v_1", "--verbose_1", help="increase output verbosity",
action="store_true")
args = parser.parse_args()
To write this code in Jupyter notebook write this:
import argparse
args = argparse.Namespace(verbose=False, verbose_1=False)
Note: In python, you can pass arguments on runtime but in the Jupyter notebook that will not be the case so be careful with the data types of your arguments.
If arguments passed by the iPython environment can be ignored (do not conflict with the specified arguments), then the following works like a charm:
# REPLACE args = parser.parse_args() with:
args, unknown = parser.parse_known_args()
From: https://stackoverflow.com/a/12818237/11750716
If you don't want to change any of the arguments and working mechanisms from the original argparse function you have written or copied.
To let the program work then there is a simple solution that works most of the time.
You could just install jupyter-argparser using the below command:
pip install jupyter_argparser
The codes work without any changes thanks to the maintainer of the package.
TL;DR I need to get a user input that contains an argument in order to do something, I need my own script that gets user input, and acts like it's its own interpreter.
My goal is to make my own CLI with my own commands.
What I need now is to get user input within a python script. The grammar for my CLI is below: (The thing I don't know how to do)
COMMAND + ARGUMENT1 + ARGUMENT2 + ARGUMENT3
Example of what I want to do:
say "hi this is a test"
hi this is a test
I have a plan for how I can make the commands with arguments,
I make a folder named 'bin' and I put python scripts in them.
Inside the python scripts are functions.
Depending on the command type, either I call the functions do do something, or it prints a output.
But for now, I need to know HOW to get user input with ARGUMENTS
The built-in argparse module as #ToTheMax said can create complex command line interfaces.
By default argparse.ArgumentParser.parse_args() will read the command line arguments to your utility from sys.argv, but if you pass in an array, it will use it instead.
You can lex (split into an array of "words") a string just like the shell is using shlex.split() which is also built in. If you use quotation marks like in your example, the words between them won't be split apart, just as in the shell.
Here's a complete example. Refer to the documentation, because this is a bit of an advance usage of argparse. There is a section that talks about "subcommands" which is what this example is based on.
import argparse
import shlex
def do_say(args):
print(args.what)
parser = argparse.ArgumentParser()
subparsers = parser.add_subparsers()
say_command = subparsers.add_parser('say')
say_command.add_argument('what')
say_command.set_defaults(func=do_say)
command = 'say "hi this is a test"'
args = parser.parse_args(shlex.split(command))
args.func(args)
The cmd module is another built-in way to make a command prompt, but it doesn't do the parsing for you, so you'd maybe combine it with argparse and shlex.
I realize I already have a question that is answered.
You can find it here:
How do you have an input statement with multiple arguments that are stored into a variable?
Here is the correct code:
def command_split(text:str) -> (str,str):
"""Split a string in a command and any optional arugments"""
text = text.strip() # basic sanitize input
space = text.find(' ')
if space > 0:
return text[:space],text[space+1:]
return text,None
x = input(":>")
command,args = command_split(x)
# print (f'command: "{command:}", args: "{args}"')
if command == 'echo':
if args == None:
raise SyntaxError
print (args)
A more simple way:
x = input(":>")
if x.split(" ")[0] == 'echo':
echoreturn = ' '.join(x.split(" ")[1:])
print(echoreturn)
My version to #rgov 's post: (Thank you!)
import argparse
import shlex
def do_say(args):
print(args.what)
parser = argparse.ArgumentParser()
subparsers = parser.add_subparsers()
say_command = subparsers.add_parser('say')
say_command.add_argument('what')
say_command.set_defaults(func=do_say)
while True:
try:
command = input(":>")
args = parser.parse_args(shlex.split(command))
args.func(args)
except SyntaxError:
print("Syntax Error")
except ValueError:
print("Value Error")
except:
print("")
I am trying to pass BioPython sequences to Ilya Stepanov's implementation of Ukkonen's suffix tree algorithm in iPython's notebook environment. I am stumbling on the argparse component.
I have never had to deal directly with argparse before. How can I use this without rewriting main()?
By the by, this writeup of Ukkonen's algorithm is fantastic.
An alternative to use argparse in Ipython notebooks is passing a string to:
args = parser.parse_args()
(line 303 from the git repo you referenced.)
Would be something like:
parser = argparse.ArgumentParser(
description='Searching longest common substring. '
'Uses Ukkonen\'s suffix tree algorithm and generalized suffix tree. '
'Written by Ilya Stepanov (c) 2013')
parser.add_argument(
'strings',
metavar='STRING',
nargs='*',
help='String for searching',
)
parser.add_argument(
'-f',
'--file',
help='Path for input file. First line should contain number of lines to search in'
)
and
args = parser.parse_args("AAA --file /path/to/sequences.txt".split())
Edit: It works
Using args = parser.parse_args(args=[]) would solve execution problem.
or you can declare it as class format.
class Args:
data = './data/penn'
model = 'LSTM'
emsize = 200
nhid = 200
args=Args()
I've had a similar problem before, but using optparse instead of argparse.
You don't need to change anything in the original script, just assign a new list to sys.argv like so:
if __name__ == "__main__":
from Bio import SeqIO
path = '/path/to/sequences.txt'
sequences = [str(record.seq) for record in SeqIO.parse(path, 'fasta')]
sys.argv = ['-f'] + sequences
main()
If all arguments have a default value, then adding this to the top of the notebook should be enough:
import sys
sys.argv = ['']
(otherwise, just add necessary arguments instead of the empty string)
I ended up using BioPython to extract the sequences and then editing Ilya Steanov's implementation to remove the argparse methods.
import imp
seqs = []
lcsm = imp.load_source('lcsm', '/path/to/ukkonen.py')
for record in SeqIO.parse('/path/to/sequences.txt', 'fasta'):
seqs.append(record)
lcsm.main(seqs)
For the algorithm, I had main() take one argument, his strings variable, but this sends the algorithm a list of special BioPython Sequence objects, which the re module doesn't like. So I had to extract the sequence string
suffix_tree.append_string(s)
to
suffix_tree.append_string(str(s.seq))
which seems kind of brittle, but that's all I've got for now.
I face a similar problem in invoking argsparse, the string '-f' was causing this problem. Just removing that from sys.srgv does the trick.
import sys
if __name__ == '__main__':
if '-f' in sys.argv:
sys.argv.remove('-f')
main()
Clean sys.argv
import sys; sys.argv=['']; del sys
https://github.com/spyder-ide/spyder/issues/3883#issuecomment-269131039
Here is my code which works well and I won't worry about the environment changed.
import sys
temp_argv = sys.argv
try:
sys.argv = ['']
print(sys.argv)
args = argparse.parser_args()
finally:
sys.argv = temp_argv
print(sys.argv)
Suppose you have this small code in python:
import argparse
parser = argparse.ArgumentParser()
parser.add_argument("-v", "--verbose", help="increase output verbosity",
action="store_true")
parser.add_argument("-v_1", "--verbose_1", help="increase output verbosity",
action="store_true")
args = parser.parse_args()
To write this code in Jupyter notebook write this:
import argparse
args = argparse.Namespace(verbose=False, verbose_1=False)
Note: In python, you can pass arguments on runtime but in the Jupyter notebook that will not be the case so be careful with the data types of your arguments.
If arguments passed by the iPython environment can be ignored (do not conflict with the specified arguments), then the following works like a charm:
# REPLACE args = parser.parse_args() with:
args, unknown = parser.parse_known_args()
From: https://stackoverflow.com/a/12818237/11750716
If you don't want to change any of the arguments and working mechanisms from the original argparse function you have written or copied.
To let the program work then there is a simple solution that works most of the time.
You could just install jupyter-argparser using the below command:
pip install jupyter_argparser
The codes work without any changes thanks to the maintainer of the package.
For some reason, I have non standard command line options for my program. My program takes in a long option also with a single '-'. For example, a valid command line option would be '-f' / '-foo'. Both short and long options need to have an argument separated by space or an '='.
I am trying to parse this using the optparse, but I understand that optparse does not support non GNU-standard options. Is there a way to tweak optparse to do this?
Here's a mildly hackish way to do what you need.
Subclass Option and OptionParser and patch some of the methods:
from optparse import Option, OptionError, OptionParser
class MyOption(Option):
def _set_opt_strings(self, opts):
for opt in opts:
if len(opt) < 2:
raise OptionError(
"invalid option string %r: "
"must be at least two characters long" % opt, self)
elif len(opt) == 2:
self._short_opts.append(opt)
else:
self._long_opts.append(opt)
class MyOptionParser(OptionParser):
def _process_args(self, largs, rargs, values):
while rargs:
arg = rargs[0]
if arg == "--":
del rargs[0]
return
elif arg[0:2] == "--":
self._process_long_opt(rargs, values)
elif arg[:1] == "-" and len(arg) > 1:
if len(arg) > 2:
self._process_long_opt(rargs, values)
else:
self._process_short_opts(rargs, values)
elif self.allow_interspersed_args:
largs.append(arg)
del rargs[0]
else:
return
Now you can do
parser = MyOptionParser()
parser.add_option(MyOption("-f", "-file", dest="filename",
help="write report to FILE", metavar="FILE"))
parser.add_option(MyOption("-q", "-quiet",
action="store_false", dest="verbose", default=True,
help="don't print status messages to stdout"))
With this, parser will accept -file as an option (and will not accept e.g. -fq).
From the optparse documentation
option:
an argument used to supply extra information to guide or customize the execution of a program. There are many different syntaxes for options; the traditional Unix syntax is a hyphen (“-”) followed by a single letter, e.g. -x or -F. Also, traditional Unix syntax allows multiple options to be merged into a single argument, e.g. -x -F is equivalent to -xF. The GNU project introduced -- followed by a series of hyphen-separated words, e.g. --file or --dry-run. These are the only two option syntaxes provided by optparse.
(emphasis added)
So no, you cannot specify other ways of handling arguments with optparse. You can, however, parse the arguments yourself by using argv from the sys module.
This is going to be more work, but it might look something like:
from sys import argv
for arg in argv:
if arg.startswith("-") or arg.startswith("--"):
# Parse the argument
I don't think there's any way to tweak optparse (though I don't know for certain), but getopt is your alternative that will handle C style command-line options.
I am just starting with python so I am struggling with a quite simple example. Basically I want pass the name of an executable plus its input via the command line arguments, e.g.:
python myprogram refprogram.exe refinput.txt
That means when executing myprogram, it executes refprogram.exe and passes to it as argument refinput. I tried to do it the following way:
import sys, string, os
print sys.argv
res = os.system(sys.argv(1)) sys.argv(2)
print res
The error message that I get is:
res = os.system(sys.argv(1)) sys.argv(2)
^
SyntaxError: invalid syntax
Anyone an idea what I am doing wrong?
I am running Python 2.7
This line
res = os.system(sys.argv(1)) sys.argv(2)
Is wrong in a couple of ways.
First, sys.argv is a list, so you use square brackets to access its contents:
sys.argv[1]
sys.argv[2]
Second, you close out your parentheses on os.system too soon, and sys.argv(2) is left hanging off of the end of it. You want to move the closing parenthesis out to the very end of the line, after all of the arguments.
Third, you need to separate the arguments with commas, a simple space won't do.
Your final line should look like this:
res = os.system(sys.argv[1], sys.argv[2])
A far, far better way to do this is with the argparse library. The envoy wrapper library makes subprocess easier to work with as well.
A simple example:
import argparse
import envoy
def main(**kwargs):
for key, value in kwargs.iteritems():
print key, value
cmd = '{0} {1}'.format(kwargs['program'], ' '.join(kwargs['infiles']))
r = envoy.run(cmd)
print r.std_out
print r.std_err
if __name__ == '__main__':
parser = argparse.ArgumentParser(description='Get a program and run it with input', version='%(prog)s 1.0')
parser.add_argument('program', type=str, help='Program name')
parser.add_argument('infiles', nargs='+', type=str, help='Input text files')
parser.add_argument('--out', type=str, default='temp.txt', help='name of output file')
args = parser.parse_args()
main(**vars(args))
This reads in the arguments, parses them, then sends them to the main method as a dictionary of keywords and values. That lets you test your main method independently from your argument code, by passing in a preconstructed dictionary.
The main method prints out the keywords and values. Then it creates a command string, and passes that to envoy to run. Finally, it prints the output from the command.
If you have pip installed, envoy can be installed with pip install envoy. The easiest way to get pip is with the pip-installer.
sys.argv is a list, and is indexed using square brackets, e.g. sys.argv[1]. You may want to check len(sys.argv) before indexing it as well.
Also, if you wanted to pass parameters to os.system(), you might want something like os.system(' '.join(sys.argv[1:])), but this won't work for arguments with spaces. You're better off using the subprocess module.
sys.argv is a list
import sys, string, os
print sys.argv
res = os.system(sys.argv[1]) sys.argv[2]
print res
If you are running Python 2.7 it is recommended to use the new subprocess module.
In this case you would write
import sys, subprocess
result = subprocess.check_output(sys.argv[1], sys.argv[2])