Error in default programming of Google's Python exercise 'Copyspecial' - python

I get an IndexError: list index out of range error when using the --todir option in Google's Python Exercise copyspecial.py. How can I resolve this issue? What confuses me the most is that the part of code causing it is what was written by the instructor (from Google/Standford). I can only assume some syntactic error has spilled into other lines of code or that built in function syntax has changed since Python 2.7. This exercise code was written in 2.7.
The file works when no option is used, as so:
Printing list of special files
C:.\gpe\copyspecial\xyz__hello__.txt
C:.\gpe\copyspecial\zz__something__.jpg
done
This is the error:
The code:
def main():
# This basic command line argument parsing code is provided.
# Add code to call your functions below.
# Make a list of command line arguments, omitting the [0] element
# which is the script itself.
args = sys.argv[1:]
if not args:
print "usage: [--todir dir][--tozip zipfile] dir [dir ...]";
sys.exit(1)
# todir and tozip are either set from command line
# or left as the empty string.
# The args array is left just containing the dirs.
todir = ''
if args[0] == '--todir':
todir = args[1]
del args[0:2]
tozip = ''
if args[0] == '--tozip':
tozip = args[1]
del args[0:2]
if len(args) == 0:
print "error: must specify one or more dirs"
sys.exit(1)
# +++your code here+++
# Call your functions
All the aforementioned code is straight from google.com. My code comes before main() is defined and after where it says # +++your code here+++
I have spent hours trying to resolve this. I've learned a lot, but not the solution.
I've tried changing indentations.
I've tried doing sys.exit(1) nest under the '--todir' 'if', but the program keeps running down into the 'if tozip' part, which leads me to believe it's syntactical. But I can't find a misplaced () or :. I also checked indentations.
I've tried adding an 'if args[0]:' check, but it doesn't work, because as I later learned, although an empty list ('args[0]' = []), Python does not interpret it as an actual 'False' value.
The list goes on
I really appreciate the opportunity to have my question heard by the community at stackoverflow, and even more so as a first time poster.

As far as I can see your third try should work if you do it right:
tozip = ''
if args and args[0] == '--tozip':
tozip = args[1]
del args[0:2]
This actually checks the list args. If it is empty ([]), it is considered False and the second test args[0] == '--tozip' does not get evaluated.
Your problem is that args itself is an empty list which does evaluate to False (see https://docs.python.org/2/library/stdtypes.html#truth-value-testing), hence you cannot access args[0] and checking for it results in the same Indexerror.
However, you would still get an IndexError if you only pass one of the parameters without the argument because you access args[1] without testing.
EDIT (Why doesn't the code run as is?): I don't think any python versions >=2.4 would interpret this differently but I have no proof. This argument passing is very basic. Checking for malformed user input is always quite "annoying" because you have to handle every possible input which results in a lot of code. If you want to go into more detail of argument passing I recommend the argparse module (2.7, 3.5). My feeling is that to avoid having a large part of the exercise file that has nothing to do with the exercise they just left it that simple. If you don't supply at least one file path as a parameter you will get an error message in the next step anyway:
if len(args) == 0:
print "error: must specify one or more dirs"
sys.exit(1)
So the code does run as is. You just have to supply the right parameters.

Related

How do I suppress an argument when nothing is input on command line?

#!/usr/bin/env python3
import argparse
parser = argparse.ArgumentParser()
parser.add_argument('--selection', '-s')
parser.add_argument('--choice', '-c', default = argparse.SUPPRESS)
args = parser.parse_args()
def main(selection, choice):
print(selection)
print(choice)
if __name__=='__main__':
main(args.selection, args.choice)
The example provided is just to provide something simple and short that accurately articulates the actual problem I am facing in my project. My goal is to be able to ignore an argument within the code body when it is NOT typed into the terminal. I would like to be able to do this through passing the argument as a parameter for a function. I based my code off of searching 'suppress' in the following link: https://docs.python.org/3/library/argparse.html
When I run the code as is with the terminal input looking like so: python3 stackquestion.py -s cheese, I receive the following error on the line where the function is called:
AttributeError: 'Namespace' object has no attribute 'choice'
I've tried adding the following parameter into parser like so:
parser = argparse.ArgumentParser(argument_default=argparse.SUPPRESS)
I've also tried the above with
parser.add_argument('--choice', '-c')
But I get the same issue on the same line.
#Barmar answered this question in the comments. Using 'default = None' in parser.add_argument works fine; The code runs without any errors. I selected the anser from #BorrajaX because it's a simple solution to my problem.
According to the docs:
Providing default=argparse.SUPPRESS causes no attribute to be added if the command-line argument was not present:
But you're still assuming it will be there by using it in the call to main:
main(args.selection, args.choice)
A suppressed argument won't be there (i.e. there won't be an args.choice in the arguments) unless the caller specifically called your script adding --choice="something". If this doesn't happen, args.choice doesn't exist.
If you really want to use SUPPRESS, you're going to have to check whether the argument is in the args Namespace by doing if 'choice' in args: and operate accordingly.
Another option (probably more common) can be using a specific... thing (normally the value None, which is what argparse uses by default, anyway) to be used as a default, and if args.choice is None, then assume it hasn't been provided by the user.
Maybe you could look at this the other way around: You want to ensure selection is provided and leave choice as optional?
You can try to set up the arguments like this:
parser = argparse.ArgumentParser()
parser.add_argument('--selection', '-s', required=True)
parser.add_argument('--choice', '-c')
args = parser.parse_args()
if __name__ == '__main__':
if args.choice is None:
print("No choice provided")
else:
print(f"Oh, the user provided choice and it's: {args.choice}")
print(f"And selection HAS TO BE THERE, right? {args.selection}")

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)

Python: Subprocess works different to terminal. What I have to change?

I have to Python scripts: Tester1.py and Tester2.py.
Within Tester1 I want to start from time to time Tester2.py. I also want to pass Tester2.py some arguments. At the moment my code looks like this:
Tester1:
subprocess.call(['python3 Tester2.py testString'])
Tester2:
def start():
message = sys.argv[1]
print(message)
start()
Now my problem: If I run with my terminal Tester2 like 'python3 Tester2.py testString'my console prints out testString. But if I run Tester1 and Tester1 tries to start Tester2, I get an IndexError: "list index out of range".
How do I need to change my code to get everything working?
EDIT:
niemmi told me that I have to change my code to:
subprocess.call(['python3', 'Tester2.py', 'testString'])
but now I get a No such file or directory Error although both scripts are in the same directory. Someone knows why?
You need to provide the arguments either as separate elements on a list or as a string:
subprocess.call(['python3', 'Tester2.py', 'testString'])
# or
subprocess.call('python3 Tester2.py testString')
Python documentation has following description:
args is required for all calls and should be a string, or a sequence of program arguments. Providing a sequence of arguments is generally preferred, as it allows the module to take care of any required escaping and quoting of arguments (e.g. to permit spaces in file names). If passing a single string, either shell must be True (see below) or else the string must simply name the program to be executed without specifying any arguments.

Bubble sort in Python not sorting properly

I have to implement bubble sort as a homework and my python script has to look for 2 command line parameters:
-f that specifies the file path of the input file that contains a number on each line that I have to sort using bubble sort;
-p that, if specified, tells the script to print the sorted list of numbers in the command line.
Also, I have to implement the algorithm in situ, which means I have to only use one list/array/etc without allocating any other temporary list/array/etc or variable to hold one or a part of all the numbers to sort in the algorithm. So, in my script, I only use unsortedList and nothing else to hold the numbers to sort. I have taken the bubble sort algorithm from the following link: Bubble Sort Homework .
Here is my script:
import sys, getopt
def main(argv):
inputFilePath = ""
printList = False
# Traitement pour parser les arguments
try:
opts, args = getopt.getopt(argv, "f:p")
except getopt.GetoptError:
usage()
sys.exit()
for opt, arg in opts:
if opt in ("-f"):
inputFilePath = arg
if opt in ("-p"):
printList = True
inputFile = open(inputFilePath, "r")
unsortedList = [line.rstrip('\n') for line in inputFile]
sortedList = bubble(unsortedList)
if printList == True:
print (sortedList)
def usage():
print ("""
Usage: bubble.py -f <filepath> -p
-f <filepath> [REQUIRED]: specifies the filepath of the input file
-p [OPTIONAL]: specifies whether to print the sorted list or not
""")
# Function found at https://stackoverflow.com/questions/895371/bubble-sort-homework
def bubble(unsortedList):
length = len(unsortedList) - 1
isSorted = False
while not isSorted:
isSorted = True
for i in range(length):
if unsortedList[i] > unsortedList[i+1]:
isSorted = False
unsortedList[i], unsortedList[i+1] = unsortedList[i+1], unsortedList[i]
return unsortedList
if __name__ == "__main__":
main(sys.argv[1:])
I am having 2 problems with my script:
First, if I do not specify the -f parameter, the script never runs the usage() function, it only tells "No such file or directory: ''". Why isn't my script running the usage() function?
Also, the bubble sort algorithm doesn't seem to work properly. If I run the script, the numbers aren't sorted properly. I can, for example, see 3998 before 403 in the list. But, I have noticed that the numbers ARE sorted, but only from the left of the numbers. For example, I can see 2553, 256, 2562. 256 is clearly not bigger than 2553, but if you take the number from the left, the third character from the left, 6, is bigger than the third character from the left of 2553, which is 5.
How can I solve those two problems?
Thanks for your help.
First, if I do not specify the -f parameter, the script never runs the usage() function, it only tells "No such file or directory: ''". Why isn't my script running the usage() function?
getopt() doesn't know which flags are required and which are optional. It merely checks that you don't pass unspecified flags or omit an argument if a flag requires one with :.
It's up to you to check that -f is passed in if you require it.
Also, the bubble sort algorithm doesn't seem to work properly. If I run the script, the numbers aren't sorted properly. I can, for example, see 3998 before 403 in the list. But, I have noticed that the numbers ARE sorted, but only from the left of the numbers.
That's because your code is actually sorting strings rather than numbers, so it's putting them in lexicographic order. Try converting them to numbers when you read the file in:
unsortedList = [int(line.rstrip('\n')) for line in inputFile]
^^^^ ^
Also, I have to implement the algorithm in situ, which means I have to only use one list/array/etc without allocating any other temporary list/array/etc or variable to hold one or a part of all the numbers to sort in the algorithm.
In that case, I'd recommend removing the return statement from the bubble sort function. The best way to show that you're only using one array is not to create a second variable called sortedList.
bubble(unsortedList)
If your bubble sort call is simply that, without the assignment, then it's clear you must be modifying the original array.

Python if statement returning

I am having trouble with what is presumably a simple if statement. I am trying to pass through the type of VM file format to use. Even if I put VMDK or VHD, it still comes back with VMDK is an invalid type or VHD is an invalid type.
import sys
vmtype = sys.argv[3]
vmtype = str(vmtype).strip().upper()
## File format check
if vmtype is not ("VHD") or ("VMDK"):
print vmtype + " is an invalid type"
sys.exit()
I have tried the if statement with != and not putting the parameters in parentheses. I have searched the web for a while and have tried what I have found and am still running into the same issue.
FYI I am running Python 2.6.5
Try:
if vmtype not in ("VHD", "VMDK"):
Your current code parses as:
if (vmtype is not ("VHD")) or ("VMDK"):
Which is obviously wrong. Since ("VMDK") is a truthy value, the whole statement always be true. Therefore the if statement will always execute.
Note that even if you tried
if vmtype is not "VHD":
It would not work, because is tests identity, not value. You would use:
if vmtype != "VHD":

Categories

Resources