Simple sort program from the command line - python

I am trying to prevent this simple sort program to accept only 1 argument when printing in reverse. this is what I type and it still goes thru:
python sort.py alpha -r
here is my whole program:
example: python sort.py able spencer delta
to reverse:
python sort.py -r able spencer delta
python sort.py --reverse able spencer delta
import argparse
import sys
def wordsorter():
argparser = argparse.ArgumentParser()
argparser.add_argument('user_string', nargs='*')
argparser.add_argument("-r","--
reverse",action="store_true",default="False",dest="z_to_a")
args = argparser.parse_args()
if (len(sys.argv)==1 or len(sys.argv)==2):
print("Invalid command line arguments to program. Please, supply two or more strings to sort.")
sys.exit(1)
if args.z_to_a == True and len(sys.argv)>1:
words_to_sort= sorted(args.user_string, reverse = True)
print(*words_to_sort)
print("if 1")
else:
words_to_sort = sorted(args.user_string)
print(*words_to_sort)
print("if 2")
if __name__ == '__main__':
wordsorter ()
When I enter one Argument without sorting in reverse it works
If I try to use reverse with one argument it should not work.
What am I doing wrong?

Your problem is caused by the fact that you tell argparse to expect any number of arguments for user_string instead of at least two, and then check that len(sys.argv) >= 2 (by the way, this is a better way to write that if condition), but it can be equal to 2 if you pass -r and only one argument for user_string.
You can check that len(args.user_string) >= 2 and that should work.
If you want to do it through argparse: Unfortunately, while it is possible to tell it to expect at least one argument, it is not possible to directly tell it to expect at least two. A possible workaround is described in this answer.

Related

Get user input with arguments in Python

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("")

Setting optional system arguments via command prompt

I am doing a project in which I want to specify one system argument on my cmd right after the script.py. My problem is that I want to specify another argument in which is optional, and the user may or may not want to give that argument. Therefore, I am struggling how to deal with the fact that the system argument might or might not be given by the user and how to read that. If everything sounds confusing, the following text might clarify:
The user types the following on the command prompt to run the program:
python script.py file.txt
I want to add an argument which may or may not be given, like:
python script.py file.txt file_added.txt
As I read these arguments on my main script, I though that this problem would solve:
If sys.argv[2] is not None:
file2 = f"\{sys.argv[2]}"
However, I still getting IndexError when doing that. So, is there a simple way to bypass such problem?
If sys.argv holds less than 2 items, you'll get an IndexError. So wrap the statement around with a try block
try:
filename = sys.argv[2]
except IndexError:
filename = None
if filename:
# ... do something
A way to accomplish this would be to check the length of sys.argv. If the length is 3 you'll know that a second argument was passed (3 because the first argument is script.py). So something along the lines:
if len(sys.argv) == 3:
file2 = f"\{sys.argv[2]}"
Here, sys.argv[2] is not None you are checking if 3rd element is None or not and that is the issue.
You are indexing outside the length of argv array and index error.
If you only have max 2 input then you could check the length of argv like if len(sys.argv) == 3 and that means you have got both the input and then you can access them via sys.argv[1] and sys.argv[2]
You can use argsparse which is a built in library in python, which makes it easy to handle command line arguments. Go to the link https://docs.python.org/3/library/argparse.html to know mor, but the basic implementation for your usecase will be like this.
import argparse
parser = argparse.ArgumentParser(description='Enter filenames')
parser.add_argument('-file', type=str,help='enter the file name',dest='filename1')
parser.add_argument('--optional','--op',type=str, dest='filename2',help='enter optional filename')
args = parser.parse_args()
file1=args.filename1
file2=args.filename2
Then in the cmd you can invoke it as
python script.py -filename="file1.txt"
or
python script.py -filename="file1.txt" --optional="file2.txt"
or
python script.py -filename="file1.txt" --op="file2.txt"
You are looking for argv[1], argv[2], and so on.
This should work:
for filename in sys.argv[1:]:
readfile(filename)

Program quits without taking user input in argparse example

I am new to python. I tried to run below python code in pycharm but without taking user input it exits. Any help?
import math
import argparse
parse = argparse.ArgumentParser(description='calculate the area of a cylinder')
parse.add_argument('radius', type=int, help='Radius of the Cylinder')
parse.add_argument('height', type=int, help='Height of the Cylinder')
args = parse.parse_args()
def cylinder_volume(radius, height):
vol = math.pi * (radius ** 2) * (height)
return vol
if __name__ == '__main__':
print(cylinder_volume(args.radius, args.height))
age = input("take input")
print("The input is",age)
#C:\Users\manoj\venv\argparse_demo\Scripts\python.exe C:/Users/manoj/PycharmProjects/argparse_demo/new.py
#usage: new.py [-h] radius height
#new.py: error: the following arguments are required: radius, height
#Process finished with exit code 2
It seems quite a redundat use of both command line argument and input(). I think doing examples with input() take syou on a wrong path in python programming, they also make bad question of StackOverflow with undetermined/unreplicable results. I was trying to make this point here earlier, but with very limited support from community.
In this particular case the line swith input seem very redundant. You just seem to learn passing the arguments through command line and then you want to play with input() - which seems a step back.
argparse takes only sys.argv arguments. To use argparse, you need to specify your arguments through command line, like: python new.py argument1 argument2
If you want to use input() for interactive purposes, pass the return value of input() to your function and don't use argparse.

Using sys.argv for input through Terminal

I wrote a Python script that I am now trying to get to run via the command line. It consists of a function that takes one obligatory and a few optional arguments.
def main(input_folder, iterations = 1000, probability_cutoff = - 40 , threshold = 10): ...
Now I am trying to make it executable through the command line like so:
if __name__ == "__main__":
main(sys.argv[1])
This works well as long as I put in only one argument; but I don't know how to accept the additional, optional input that sys.argv delivers as a list.
Is there a simple way of doing this with this approach?
Or is it necessary to use an additional module such as
argparse?
I tried feeding keyword arguments into the function as well but
couldn't make that work either - is that a feasible approach?
I am working with Python 2.7 on a Mac. Any help is much appreciated!
I always use argparse, because it gives you nice error handling, converts strings to ints or open files, and clearly documents the options. However, this should do what you want:
if __name__ == "__main__":
main(*sys.argv[1:])

python sys.argv differentiate int and string

Simply put, how can I differentiate these two in test.py:
python test.py 1
python test.py '1'
Workaround is OK.
Edit:
This workaround looks cool but too complex: argparse
Let the invoker specify args later, in python code use arg = input('Please enter either an integer or a string')
And other workarounds as presented in the answers of this question.
Thank you all for the replies. Every body +1.
The quotes are consumed by the shell. If you want to get them into python, you'll have to invoke like python test.py 1 "'2'" "'3'" 4
It is common handling of args, performed by shell. " and ' are ignored, since you may use them to pass, for instance, few words as one argument.
This means that you can't differentiate '1' and 1 in Python.
The shell command line doesn't support passing arguments of different types. If you want to have commands with arguments of different types you need to write your own command line or at least your own command parser.
Variant 1:
Usage:python test.py "1 2 '3' '4'"
Implementation:
command = sys.argv[1]
arguments = map(ast.literal_eval, command.split())
print arguments
Variant 2:
Usage:
python test.py
1 2 '3' 4'
5 6 '7' 8'
Implementation:
for line in sys.stdin:
arguments = map(ast.literal_eval, line.split())
print arguments
(Of course, you'd probably want to use raw_input to read the command lines, and readline when it is available, that's merely an example.)
A much better solution would be to actually know what kind of arguments you're expected to get and parse them as such, preferably by using a module like argparse.
Windows-specific:
# test.py
import win32api
print(win32api.GetCommandLine())
Example:
D:\>python3 test.py 3 "4"
C:\Python32\python3.EXE test.py 3 "4"
You can then parse the command line yourself.
As you can see from your experiment, the quotes are gone by the time Python is invoked. You'll have to change how the Python is invoked.
I'm not sure how correct I am, but if you're using only integer command line arguments, you can typecast it to be int.
suppose (in *nix), I run my program as:
./test.py 1
I can in my program say something line
import sys
def main():
a=int(sys.argv[1])

Categories

Resources