Why is the setname function running? - python-3.x

I am trying to create a multipurpose program using Python3.
I have tested def weather(): and it works to show weather data for a given zip code. The program is supposed to never end unless the user types STOP.
The setname(): function is only supposed to run if the user types 'name' and 'my' or 'set'. When I run the program it asks, "What would you like to be called" no matter what I type. I tried even just typing z, but it still somehow started up the setname(): function. Please forgive me as this is my first time asking on stack.
What is causing the setname(): function to activate?
def weather():
import requests
wgURL = "http://api.wunderground.com/api/0def10027afaebb7/forecast/q/f/place.json"
print('What place are you looking for?')
wgURL = wgURL.replace('place', input())
r = requests.get(wgURL)
data = r.json()
for day in data['forecast']['simpleforecast']['forecastday']:
print (day['date']['weekday'] + ":")
print ("Conditions: ", day['conditions'])
print ("High: ", day['high']['fahrenheit'] + "F", "Low: ", day['low']['fahrenheit'] + "F", '\n')
def setname():
print('What would you like to be called?')
username = input()
print('Okay, I will now call you ' + username)
username = 'Anthony'
print('Done')
run = True
while run == True:
request = input()
if request == 'hi' or 'hello':
print('Hello there ' + username)
if 'weather' in request:
weather()
if 'name' and 'my' or 'set' in request:
setname()
if request == 'STOP':
run = False
Here is a screenshot from IDLE

You misunderstand how in works. Since in binds more tightly than either and or or, it turns out that 'name' and 'my' or 'set' in request means ('name' and 'my') or ('set' in request). This always evaluates to true, since 'name' is truthy and 'my' is truthy (both are non-empty strings).
Do this instead:
if ('name' in request and 'my' in request) or ('set' in request):
setname()
You likewise have a problem with request == 'hi' or 'hello': this means (request == 'hi') or 'hello', which is always true, since 'hello' is truthy. What you probably want here is:
if request == 'hi' or request == 'hello':
Consult the operator precedence table if it strikes your fancy.

Related

How to give custom response if user input other data types?

response = input("Do you want some food? (Y/N): ")
if response == "Y":
print("Come! There is pizza for you")
elif response == "N":
print("Alright, let us do Sisha instead.")
elif response == range(999):
print("The required response is Y/N.")
else:
print("I do not understand the prompt")
Q1: How to give feedback when user input numbers instead of string?
A1: I take a look in python documentation but it seems that range cannot be used in if statement?
I try to modify the code by stating
deret_angka = int or float
for n in deret_angka:
print("The required response is Y/N.")
and third ifs condition to:
elif response == deret_angka:
print("The required response is Y/N.")
But got TypeError: 'type' object is not iterable
Q2: How to give Y & N value even if its lower case y/n?
A2: I tried to put "Y" or "y" but it doesn't work and just passed to next if condition.
It's pretty simple, you can give feedback in many ways in python.
Let's say the user typed a number so we can try to convert the type of the input and see if it raises an error like that:
response = input("Do you want some food? (Y/N): ")
try:
float(response)
int(response)
except ValueError: #it a string or a bool
print("The required response is Y/N.")
If you want to check on a number in a specific range you can do it like that:
if response in [range(999)]:
pass
You can't check just range(999) because it is an object. You need to convert this object to a list to loop over it.
To check lower case you can do it in some ways:
response = input("Do you want some food? (Y/N): ")
if response in ['Y', 'y']:
pass
elif response in ['N', 'n']:
pass
#or like that:
if response.lower = 'y': # Just convert the input to lower
pass
if response.upper = 'N': # You can also do this vice versa and convert to upper and check.
pass

Unittesting for correct 'continue' behaviour

I have a function that asks a user for confirmation via a prompt. It accepts y or n as answers, otherwise it asks again.
Now, I want to write a unittest for this function. I can test the correct behaviour for y or n just fine, but how do I test that my function correctly rejects inacceptable input?
Here's the code for foo.py:
def get_input(text):
"""gets console input and returns it; needed for mocking during unittest
"""
return input(text)
def confirm(message='Confirm?', default=False):
"""prompts for yes or no response from the user. Returns True for yes and
False for no.
'default' should be set to the default value assumed by the caller when
user simply types ENTER, and is marked in the prompt with square brackets.
"""
if default:
message = '%s [y]|n: ' % (message) # default answer = yes
else:
message = '%s y|[n]: ' % (message) # default answer = no
while True:
answer = get_input(message).lower()
if not answer:
return default
if answer not in ['y', 'n']:
print('Please enter y or n!')
continue
if answer == "y":
return True
if answer == 'n':
return False
answer = confirm()
print(answer)
And here is my Test class:
import unittest
import foo
class TestFoo_confirm(unittest.TestCase):
"""testing confirm function
"""
#unittest.mock.patch('foo.get_input', return_value='y')
def test_answer_yes(self, _):
self.assertEqual(foo.confirm(), True) # confirmed if 'y' was entered
So, how do I write a similar test for an input-value like '1' (or how do I need to adjust my confirm() function to make it testeable)?
Currently, if I call foo.confirm() from the unittest file, it just gets stuck in an infinite loop and it doesn't return anything. (I understand why this is happening, just not how to circumvent it.)
Any ideas?
You could try this:
import unittest, unittest.mock
import foo
class TestFoo_confirm(unittest.TestCase):
"""testing confirm function
"""
#unittest.mock.patch('foo.get_input', return_value='y')
def test_answer_yes(self, _):
self.assertEqual(foo.confirm(), True) # confirmed if 'y' was entered
#unittest.mock.patch('builtins.print')
#unittest.mock.patch('foo.get_input', side_effect=['1','yn','yes','y']) # this will make the mock return '1', 'yn' and so on in sequence
def test_invalid_answer(self, mock_input, mock_print):
self.assertEqual(foo.confirm(), True) # it should eventually return True
self.assertEqual(mock_input.call_count, 4) # input should be called four times
mock_print.assert_called_with('Please enter y or n!')
In the second test case, we imitate a user who enters three invalid inputs, and, after being prompted again, finally enters 'y'. So we patch foo.get_input in such a way that it returns 1 the first time it's called, then yn, then yes and finally y. The first three examples should cause the confirm function to prompt the user again. I also patched the print function, so that the 'Please enter y or n!' message wouldn't show up when testing. This isn't necessary.
Then we assert that our mock input was called four times, meaning that the first three times, the confirm function reprompted.
Finally we assert that the print function was called (at least once) with 'Please enter y or n!'.
This does not test if the correct number of print statements were made or if they were in correct order, but I suspect this would be possible too

How can I test a loop with multiple input calls?

I'm trying to test a fuction that dependets a of multiple user inputs to return some value.
I've already looked for multiples unswers here but none was able to resolve my problem. I saw things with parametrize, mock and monkey patch but none helped. I think a lot is because I don't clearly understood the concepts behind what was being done and I couldn't adapt to my problem. I saw suggestion of using external file for this but I don't wont to depend on that. I'm trying with pytest and python 3.7.3
The function that I want to test is something like this
def function():
usr_input = input('please enter a number: ')
while True:
if int(usr_input) < 5:
usr_input = input('please, enter a value less then 5: ')
else:
break
return usr_input
I want to know how can I pass two input values to test the function when the inserted value is not valid. Example: Send value 6 and 2, make an assert expecting value 2 and pass the test. My others tests look like this:
def test_input(monkeypatch):
monkeypatch.setattr('builtins.input', lambda x: 6)
test = function()
assert test == 2
but, for this case, they loop. It's possible to do this only with parametrize or other simple code?
EDIT
I added a int() in my "if", as wim pointed in the accepted answer, just to prevent any confusion for future readers. I'm assuming the cast is possible.
Two problems here, you need to convert the input into a number otherwise the comparison will fail, comparing a string with a number: usr_input < 5. Note that the real input will never return a number, only a string.
Once you've cleared that up, you can monkeypatch input with a callable that can return different values when called:
def fake_input(the_prompt):
prompt_to_return_val = {
'please enter a number: ': '6',
'please, enter a value less then 5: ': '2',
}
val = prompt_to_return_val[the_prompt]
return val
def test_input(monkeypatch):
monkeypatch.setattr('builtins.input', fake_input)
test = function()
assert test == 2
If you install the plugin pytest-mock, you can do this more easily with the mock API:
def test_input(mocker):
mocker.patch('builtins.input', side_effect=["6", "2"])
test = function()
assert test == 2

Can I patch an object method using a decorator

Challenge: Patching/mocking a method call in a remote class to return a known piece of data.
I have a whole series of tests that look like:
#pytest.mark.gen_test
def test_assignment5(app):
with patch.object(BaseHandler, 'get_current_user') as mock_user:
mock_user.return_value = {"name": '1_kiz'}
with patch.object(BaseHandler, 'get_auth_state') as mock_state:
mock_state.return_value = { "course_id": "course_2",
"course_role": "Instructor",
"course_title": "A title",
}
r = yield async_requests.get(app.url + "/assignment?course_id=course_2&assignment_id=assign_a")
assert r.status_code == 200
response_data = r.json()
assert response_data["success"] == False
assert response_data["note"] == "Assignment assign_a does not exist"
(app is the core method for my application, and get_current_user & get_auth_state use information outside the scope of the app to get a value, so need fudged)
..... the repeating with segments offend my sensibility of good-looking code.
I know I could pull the return-value dictionaries to top-level variables, and that would reduce some of the repeated code, however I'm still repeating the with patch.object stuff every time.
Reading https://docs.python.org/dev/library/unittest.mock.html#unittest.mock.patch I know I could simply decorate each test, however
user_kiz = { ... }
auth_inst = { ... }
....
#pytest.mark.gen_test
#patch('BaseHandler.get_current_user', return_value = user_kiz)
#patch('BaseHandler.get_auth_state', return_value = auth_inst)
def test_assignment5a(app, kiz):
r = yield async_requests.get(app.url + "/assignment?course_id=course_2&assignment_id=assign_a")
assert r.status_code == 200
response_data = r.json()
assert response_data["success"] == False
assert response_data["note"] == "Assignment assign_a does not exist"
just gives me a ModuleNotFoundError: No module named 'BaseHandler' error.
Is there a way to patch/mock a method call in a remote class, allowing me to set the current_user & auth_state dictionaries for each call?
(ultimately, I will also need to test for different users, and different auth_states)
To replace a call like this:
with patch.object(a, 'b', c):
with a decorator, you'd use:
#patch.object(a, 'b', c)
You had switched to the string form
#patch(..., ...)
The string form requires the full import path as the first argument

how to pass many arguments through a command function (do_)

I want to code a simple command that could take 3 arguments for a text adventure game.
Basically at the prompt, I would type 'use key unlock door' and it would run a particular block.
Here is what I coded but it does not work:
def do_use(self, tool, action, object):
if tool == 'key':
if action == 'unlock':
if object == 'door':
print("seems like it works!")
else:
print("nope 1")
else:
print("nope 2")
else:
print("nope 3")
Notes: the rest of the commands work fine. I imported cmd
and the following is the main code:
class Game(cmd.Cmd):
def __init__(self):
cmd.Cmd.__init__(self)
....
if __name__ == "__main__":
g = Game()
g.cmdloop()
At the prompt, when I type:
>>> use key unlock door
I get the following error message:
TypeError: do_use() takes exactly 4 arguments (2 given)
The code would work if it would print:
seems like it works!
Any help will be appreciated.
Reading that documentation, it looks like all of the commands just take in a single string, and you have to parse the string yourself. Your command is defined as taking 4 arguments (including self), and cmd is calling it with self, input, which is 2. I think could get the result you want with the following:
def do_use(self, user_input):
args = user_input.split()
if len(args) != 3:
print "*** invalid number of arguments"
else:
tool, action, obj = args
# Do the rest of your code here

Resources