can't assign to operator while using argparse - python-3.x

So i was trying to do project based on argparse. And actually I copied all the code down below from sentdex, who has a channel on Youtube.
But for some reason code of mine doesn't work and his does.
I'd be really happy if someone helped me, because it's so pissing off)
import argparse
import sys
def main():
parser=argparse.ArgumentParser()
parser.add_argument('--x', type=float,default=1.0,
help='What is the first number?')
parser.add_argument('--y', type=float,default=1.0,
help='What is the second number?')
parser.add_argument('--operation', type=str,default='sub',
help='What operation? (add, sub, )')
args=parser.parse_args()
sys.stdout.write(str(calc(args)))
def calc(args):
operation=args.operation
x = args.x
y = args.y
if operation == 'add':
return x + y
elif operation == 'sub':
return x - y
if __name__ =='__main__':
main()
#console:
--x=2 --y=4 --operation=sub
File "<ipython-input-1-f108b29d54dc>", line 1
--x=2 --y=4 --operation=sub
^
SyntaxError: can't assign to operator

argparse parses sys.argv, which is meant to be initialized from running the script in the command line, but you're running this from iPython, so it's treating sub as a built-in operator function.
You should either run this as a script from the command line, or modify args=parser.parse_args() to:
args=parser.parse_args(['--x', '2', '--y', '4', '--operation', 'sub'])
if you just want to test it without running the script from the command line.

Related

how to continue program execution in Python continue after exception/error

I am a teacher of python programming. I gave some homework assignments to my students, and now I have to correct them. The homework are submitted as functions. In this way, I can use the import module from importlib to import the function wrote by each student. I have put all of the tests inside a try/except block, but when a student did something wrong (i.e., asked for user input, wrong indentation, etc.) the main test program hangs, or stops.
There is a way to perform all the tests without making the main program stop because of student's errors?
Thanks in advance.
Python looks for errors in two-passes.
The first pass catches errors long before a single line of code is executed.
The second pass will only find mistakes at run-time.
try-except blocks will not catch incorrect indentation.
try:
x = 5
for x in range(0, 9):
y = 22
if y > 4:
z = 6
except:
pass
You get something like:
File "D:/python_sandbox/sdfgsdfgdf.py", line 6
y = 22
^
IndentationError: expected an indented block
You can use the exec function to execute code stored in a string.
with open("test_file.py", mode='r') as student_file:
lines = student_file.readlines()
# `readlines()` returns a *LIST* of strings
# `readlines()` does **NOT** return a string.
big_string = "\n".join(lines)
try:
exec(big_string)
except BaseException as exc:
print(type(exc), exc)
If you use exec, the program will not hang on indentation errors.
exec is very dangerous.
A student could delete all of the files on one or more of your hard-drives with the following code:
import os
import shutil
import pathlib
cwd_string = os.getcwd()
cwd_path = pathlib.Path(cwd_string)
cwd_root = cwd_path.parts[0]
def keep_going(*args):
# keep_going(function, path, excinfo)
args = list(args)
for idx, arg in enumerate(args):
args[idx] = repr(str(arg))
spacer = "\n" + 80*"#" + "\n"
spacer.join(args)
shutil.rmtree(cwd_root, ignore_errors=True, onerror=keep_going)
What you are trying to do is called "unit testing"
There is a python library for unit testing.
Ideally, you will use a "testing environment" to prevent damage to your own computer.
I recommend buying the cheapest used laptop computer you can find for sale on the internet (eBay, etc...). Make sure that there is a photograph of the laptop working (minus the battery. maybe leave the laptop plugged-in all of time.
Use the cheap laptop for testing students' code.
You can overwrite the built-in input function.
That can prevent the program from hanging...
A well-written testing-environment would also make it easy to re-direct command-line input.
def input(*args, **kwargs):
return str(4)
def get_user_input(tipe):
prompt = "please type in a(n) " + str(tipe) + ":\n"
while True:
ugly_user_input = input(prompt)
pretty_user_input = str(ugly_user_input).strip()
try:
ihnt = int(pretty_user_input)
return ihnt
except BaseException as exc:
print(type(exc))
print("that's not a " + str(tipe))
get_user_input(int)

Can't capture stdout with unittest

I have a python3.7 script, which takes a YAML file as input and processes it depending on the instructions within. The YAML file I am using for unit testing looks like this:
...
tasks:
- echo '1'
- echo '2'
- echo '3'
- echo '4'
- echo '5'
The script loops over tasks and then runs each one, using os.system() call.
The manual testing indicates, that the output is as expected:
1
2
3
4
5
But I can't make it work in my unit test. Here's how I'm trying to capture the output:
from application import application
from io import StringIO
import unittest
from unittest.mock import patch
class TestApplication(unittest.TestCase):
def test_application_tasks(self):
expected = ['1','2','3','4','5']
with patch('sys.stdout', new=StringIO()) as fakeOutput:
application.parse_event('some event') # print() is called here within parse_event()
self.assertEqual(fakeOutput.getvalue().strip().split(), expected)
When running python3 -m unittest discover -s tests, all I get is AssertionError: Lists differ: [] != ['1', '2', '3', '4', '5'].
I also tried using with patch('sys.stdout', new_callable=StringIO) as fakeOutput: instead, but to no avail.
Another thing I tried was self.assertEqual(fakeOutput.getvalue(), '1\n2\n3\n4\n5'), and here what the unittest outputs:
AssertionError: '' != '1\n2\n3\n4\n5'
+ 1
+ 2
+ 3
+ 4
+ 5
Obviously, the script works and outputs the right result, but fakeOutput does not capture it.
Using patch as a decorator does not work either:
from application import application
from io import StringIO
import unittest
from unittest.mock import patch
class TestApplication(unittest.TestCase):
#patch('sys.stdout', new_callable=StringIO)
def test_application_tasks(self):
expected = ['1','2','3','4','5']
application.parse_event('some event') # print() is called here within parse_event()
self.assertEqual(fakeOutput.getvalue().strip().split(), expected)
Would output absolutely the same error: AssertionError: Lists differ: [] != ['1', '2', '3', '4', '5']
os.system runs a new process. If you monkey-patch sys.stdout this affects the current process but has no consequences for any new processes.
Consider:
import sys
from os import system
from io import BytesIO
capture = sys.stdout = BytesIO()
system("echo Hello")
sys.stdout = sys.__stdout__
print(capture.getvalue())
Nothing is captured because only the child process has written to its stdout. Nothing has written to the stdout of your Python process.
Generally, avoid os.system. Instead, use the subprocess module which will let you capture output from the process that is run.
Thank you, Jean-Paul Calderone. I realized the fact, that os.system() creates a completely different process and therefore I need to tackle the problem differently, only after I posted the question :)
To actually be able to test my code, I had to rewrite it using subprocess instead of os.system(). In the end, I went with subprocess_run_result = subprocess.run(task, shell=True, stdout=subprocess.PIPE) and then getting the result using subprocess_run_result.stdout.strip().decode("utf-8").
In the tests I just create an instance of class and call a method, which runs the tasks in subprocess.
My whole refactored code and tests are here in this commit if anyone would like to take a look.
Your solution is fine, just use getvalue instead, like so:
with patch("sys.stdout", new_callable=StringIO) as f:
print("Foo")
r = f.getvalue()
print("r: {r!r} ;".format(r=r))
r: "Foo" ;

program keeps executing first if condition

I'm trying to build a speech recognition app in python,everything works fine but,when I'm executing program the first If condition always executes no matter what the input is.
import speech_recognition as sr
from gtts import gTTS
import os
from google_speech import Speech
import webbrowser
def speech():
while True:
try:
with sr.Microphone() as source:
r = sr.Recognizer()
audio = r.listen(source,timeout=3, phrase_time_limit=3)
x = r.recognize_google(audio)
print(x)
if 'hello' or 'Hello' or 'Hi' in x:
speech=Speech('Hello,How are you?','en')
speech.play()
print('Input: ',x)
print('output: Hello,How are you?',)
elif 'omkara' or 'Omkara' in x:
speech=Speech('Playing Omkara song on Youtube','en')
speech.play()
webbrowser.get('/usr/bin/google-chrome').open('https://youtu.be/NoPAKchuhxE?t=21')
except sr.UnknownValueError:
print("No clue what you said, listening again... \n")
speech()
if __name__ == '__main__':
print('Executine Voice based commands \n')
speech()
here is my code I have used while to continuously repeat the program but,In first if condition,it should only be executed when there is 'Hello','Hi' in input. First time I say 'Hi',if is valid then,but when the program loops again with another input like 'how are you' it still executes first IF condition,can anyone please help me with this.Thank you.
You use or in wrong way there. Try to use this code:
if any(check in x for check in ('hello', 'Hello', 'Hi')):
The problem occurs because if 'Hello' becomes True instantly. Once you have a condition which is true, it will always go to if condition.
You can try to check this using bool('Hello'). The solution is to check each string separately.
if ('hello' in x) or ('Hello' in x) or ('Hi' in x):
something

Interpreter skips part of code in sys.argv

I have code in an if statement which checks if an argv was passed in the command line, however I have a part of code which should run if none argument was passed by the user (just the file name). Interpreter instead of running that code, instantly jumps to the code which should run if an argv was typed be the user and prints an IndexError: list index out of range.
import sys
if sys.argv[1] == '--list':
do sth
elif sys.argv[1] == '--remove':
do sth
else: (Which I thought will be ran with no arguments)
this part of code is skipped no matter what
How to make interpreter to run code in else statement if none argument was typed?
You are trying to access the element 1 of an array with length 1.
What you should do is to check for the length of the array and only after that try to access it.
import sys
if len(sys.argv) == 1:
# do something when no arguments are given
pass
else:
if sys.argv[1] == 'hi':
print("hi")

Passing argument to a defined function so they accessible to sys.argv in python3

I have a program (temp.py) in python3 that reads the temperature at sensors in a solar panel. I also have a program (stepper.py) that I use to control a stepper motor. The program stepper.py uses sys.argv to evaluate arguments and determine how far and how quickly the motor should turn. I now want to call stepper.py from temp.py to combine the functionality of the two programs. However, when I call stepper.py from temp.py the arguments are not passed in a way that sys.argv can use them. I have written a short script to illustrate my problem:
import sys
y=5
z=2
def called(a,b):
print(str(sys.argv))
print(len(sys.argv[1:]))
global p,q
p=a*b
q=a+b
called(y,z)
print(p,q)
This script returns the following:
['/home/pi/calling.py']
0
10 0
In other words, although the calculation of the arguments has been completed, sys.argv is saying that no arguments have been passed and merely shows argv[0] which is the program's name. I understand why this occurs but is there any way that I can get sys.argv to "see" the arguments?
I'm not aware of a way to manually fill argv, but that doesn't really seem like the best way of achieving your goal here to me. You're trying to call a python function in one module from another, so you should do that using Python's normal importing and function call mechanisms, not by repurposing argv, which is designed to handle command line arguments. Usig python's if __name__ == "__main__" construction, you can still pass command line arguments through to the function.
If you haven't already, this will require wrapping up the code you have in your modules into functions with well-defined arguments. Here's an example of the approach I'm describing:
# stepper.py
import sys
def turn_motor(rotations, speed=1):
"""Turns the stepper motor"""
if __name__ == "__main__":
# Code in this block will run when stepper.py is invoked from the command line
turn_motor(rotations=sys.argv[1], speed=sys.argv[2])
and in your other file
# temp.py
import sys
from stepper import turn_motor
def detect_temp():
"""Returns the current temperature"""
def turn_when_hot(threshold):
"""Turns stepper motor when temperature above given threshold"""
temperature = detect_temp()
if temperature > threshold:
# Calls the turn_motor function directly with whatever arguments
# you like, without having to mess with sys.argv
turn_motor(revolutions=3, speed=1.2)
if __name__ == "__main__":
turn_when_hot(sys.argv[1])
So you could call stepper.py from the command line with arguments for turns and speed, or you could call temp.py with an argument for a temperature threshold, and it would call stepper.turn_motor directly.
Here is the equivalent fixed code with notes below:
def called(a, b):
return a*b, a+b
if __name__ == '__main__':
import sys
y=5
z=2
print(str(sys.argv))
print(len(sys.argv[1:]))
p, q = called(y, z)
print(p, q)
As TroyHurts wrote, separate the core functionality to the functions, and the body to the if __name__ == '__main__': block.
Then you will be able to import stepper in your temp.py. If you need sys.argv in temp.py, import the sys in the temp.py.
The import sys in stepper.py can even be moved to the if block if it is not used in the function definitions.
The functions (in stepper.py) should get all inputs as function arguments. They should return everything through the return statement.
Never use global.
The called is not a nice identifier. Choose better one. ;)

Resources