Continuing from yesterday, i was implementing a basic exception handler, to deal with every type of error from the user, using conditional if and elif statements. It was fine until my program wrongly thinks that integer inputs are non-integers (for the age field), and this stops me from continuing my program, as other functions are dependant on this. Here is the code snippet:
def submit():
username = UserName.get()
firstname = User_FirstName.get()
surname = User_Surname.get()
age = User_Age.get()
height = User_Height.get()
weight = User_Weight.get()
data = [username, firstname, surname, age, height, weight]
flag = False
while flag == False:
if len(username) == 0:
messagebox.showerror('Project Pulse', 'Please ensure the "Username" field is not left blank')
break
elif type(firstname) != str:
messagebox.showerror('Project Pulse', 'Please ensure there are no numbers in the "First Name"')
break
elif len(firstname) == 0:
messagebox.showerror('Project Pulse', 'Please ensure that the "First Name" field is not left blank')
break
elif len(surname) == 0:
messagebox.showerror('Project Pulse', 'Please ensure that the "Last Name" field is not left blank')
break
elif type(surname) != str:
messagebox.showerror('Project Pulse', 'Please ensure there are no numbers in the "Surname"')
break
elif len(age) == 0:
messagebox.showerror('Project Pulse', 'Please ensure the "age" field is not left blank')
break
elif type(age) != int:
messagebox.showerror('Project Pulse', 'Please ensure only integers are input in "Age"')
break
...
else:
flag = True
Here, submit is a button. The elif statements continue for a few more lines, but the point is, the program does not run past the 'age' line, i.e. the error message box : 'Please ensure only integers are input in "Age" displays, no matter what. I tried printing the actual age variable, and i got an integer, so I can't seem to find the issue!
I think your issue might be that get() treats every user input as a string.
In other words, even though the user might want to say 22, the computer would read it as "22".
To safeguard against this, you can try putting int() around User_Age.get():
int(User_Age.get())
Hope this helps!
-VDizz
Like #VDizz, I am assuming your values are coming from tk.Entry widgets. Therefore all values will be strings. So the casting will work but this will also generate an error in your elif block. To avoid the error causing a new issue you might try writing a small function to check the type. As per this stack overflow response https://stackoverflow.com/a/9591248/6893713.
Hope this helps too.
In response to #Guilherme Iazzete's comment (slightly modified from the link):
def intTryParse(value):
try:
int(value)
return True
except ValueError:
return False
username = 'HarvB'
age = '4'
flag = False
while flag == False:
if len(username) == 0:
messagebox.showerror('Project Pulse', '...')
break
...
elif intTryParse(age) is False:
messagebox.showerror('Project Pulse', '...')
break
...
else:
flag = True
Related
So, I've written a function that is meant to take a password, userID, and pin, match it to an account(in the prewritten dictionary) and validate it, but if three wrong info is inputted, it shall end the while loop. However, after i input the password, user ID and pin, it just stops with exit code 0. Can anyone explain why?
info = {"password0": "FelixIscool3103",
"PIN0": "3456",
"UserID0": "Felixx",
"password1": "JohnSmith348",
"PIN1": "7845",
"UserID1": "JohnSmithy99"}
def checker(password, user_id, pin, strike_pass, strike_id, strike_pin):
while strike_id and strike_pass and strike_pin != 3:
for i in range(0, len(info), 3):
if password == info["password"+str(i)]:
return print("Correct password")
break
else:
strike_pass + 1
return print("Password is incorrect")
for x in range(2, len(info), 3):
if user_id == info["UserID"+str(i)]:
return print("Correct user ID")
break
else:
strike_id + 1
return print("Wrong user ID")
for y in range(1, len(info), 3):
if pin == info["PIN" + str(i)]:
return print("Correct user ID")
break
else:
strike_pin + 1
return print("PIN incorrect")
checker(input("Enter your password"),
input("Enter your user ID"),
input("Enter your pin"),
0, 0, 0)
The numeric value 0 is "falsey" in Python. And you're passing a 0 for strike_id and strike_pass. So the while condition is evaluating to:
while False and False and True:
Which is, of course, false.
It looks like your intent was to compare each of those values to 3, but you only compared one of them:
while strike_id != 3 and strike_pass != 3 and strike_pin != 3:
The while loop continues while the condition is True.
As strike_id and strike_pass and strike_pin != 3 is False it never enters the while loop.
I am not really sure what the statement should check, but maybe try:
limit = 3
while all((strike_id <=limit, strike_pass <= limit, strike_pin <= limit)):
However, make sure the strikes get added correctly.
Imagine that I have this list:
a=[ {'name':'John','number':'123'} , {'name':'Mike','number':'12345'} ,{'name':'Peter','number':'12'}]
name=input('insert your name')
number=input('insert your number')
If I want to have 3 scenarios: one where the name and number matches, the second where the name is correct but the number is incorrect and the last one where the name does not exist.
if {'name':name,'number':number} in a:
print('okay')
else:
if {'name':name} in a and {'number':number} not in a:
print('user okay, but incorrect pass')
else:
print('No username')
This type of code will not work, right?
So how can I solve the second step (after the first else)?
You could use any like so:
if {'name':name,'number':number} in a:
print('okay')
elif any(entry["name"] == name for entry in a):
print('user okay, but incorrect pass')
else:
print('No username')
I also simplified your else: if: to use elif: instead.
I would actually write the code like this, making use of list comprehension. It seems more readable.
if name in [pos['name'] for pos in a]:
if number == [dd for dd in a if dd['name'] == name][0]['number']:
print('okay')
else:
print('user okay but incorrect pass')
else:
print('No username')
Or alternatively, if you are ok with a for loop:
for dd in a:
if name == dd['name']:
if number == dd['number']:
print('okay')
else:
print('user okay but incorrect pass')
break
else:
print('No username')
Note the else after the for loop: this construct it's explained here.
The latter is more efficient, as it iterates over the list only once (and not all the list if a match is found).
def specificChecker(someThing, checker):
if checker == None:
return someThing
elif checker == True:
return not someThing
else:
return None
def whatDoesTheCheckerSay(someThing):
if specificChecker(someThing) == someThing:
return 'The checker value was False on that one.'
elif specificChecker(someThing) == not someThing:
return 'The checker value was True on that one.'
elif specificChecker(someThing) == None:
return 'Something irregular happend. The checker value wasn\'t None or True.'
else:
return 'Something went really wrong. This doesn\'t even not work.'
reallySomeThing = input('Type in really some thing: ')
theChecker = input('Set the checker to something: ')
print(specificChecker(reallySomeThing, theChecker))
print(whatDoesTheCheckerSay(reallySomeThing)) # This can be made more efficient, right?
def one(someShit):
return someShit + ' AWWW YEAH!'
def two(someShit):
return one(someShit)
print(two(input('Type in some kind of stuff: ')))
I'm a self-taught beginner, so surely it's something awkwardly basic. I'm using the IDLE shell and have repeatedly gotten a syntax error at the second definition statement of my codes. Please help?
You cannot use the line:
elif specificChecker(someThing) == not someThing:
This must be written
elif specificChecker(someThing) != someThing:
to be valid Python.
This is also valid but is perhaps less readable:
elif (specificChecker(someThing)) == (not someThing):
After OP edit:
The new error is the mismatch in arguments (always 1) to a function that requires 2 arguments. You have to pass two arguments to specificChecker not one
Line 12: elif specificChecker(someThing) == not someThing:
If you want to check if some variable is not some variable, used is not for boolean or != for values and strings
I'm often tasked with asking users for input. I've always just written my prompts "as-needed" in my main execution scripts. This is kind of ugly, and because I often ask for the same types of input across multiple scripts, a ton of my code is just copy/pasted prompt loops. Here's what I've done in the past:
while True:
username = input("Enter New Username: ")
if ldap.search(username):
print " [!] Username already taken."
if not validator.validate_username(username):
print " [!] Invalid Username."
else:
break
I'd like to create something that can be called like:
username = prompt(prompt="Enter New Username: ",
default=None,
rules=["user_does_not_exist",
"valid_username"])
Then the prompt function looks like:
def prompt(prompt, default, rules):
while True:
retval = input(prompt)
if default and retval == "":
break
return default
if not rule_match(retval, rules):
continue
break
return retval
def rule_match(value, rules):
if "user_does_not_exist" in rules:
if not user.user_exists(value):
return False
if "valid_username" in rules:
if not validator.username(value):
return False
if "y_n_or_yes_no" in rules:
if "ignore_case" in rules:
if value.lower() not in ["y", "yes", "n", "no"]:
return False
else:
if value not in ["y", "yes", "n", "no"]:
return False
return True
An alternative I'm considering is to make a Prompt class, which would allow for more flexibility with the results. For example, if I want to convert the "y" or "n" to True or False, the above doesn't really work.
create_another = Prompt(prompt="Create another user? (y/n): ,"
default=False,
rules=["y_n_or_yes_no",
"ignore_case"]).prompt().convert_to_bool()
The other alternative I'm considering is just making individualized prompts and naming them, with each one written similar to my very original code. This doesn't actually change anything. It just serves to get these loops out of my main execution code which makes the main execution code easier to browse:
username = prompt("get_new_username")
def prompt(prompt_name):
if prompt_name == "get_new_username":
while True:
username = input("Enter New Username: ")
if ldap.search(username):
print " [!] Username already taken."
if not validator.validate_username(username):
print " [!] Invalid Username."
else:
break
return username
if prompt_name == "y_n_yes_no_ignore_case":
# do prompt
if prompt_name == "y_n_yes_no":
# do prompt
if prompt_name == "y_n":
# do prompt
if prompt_name == "y_n_ignore_case":
# do prompt
if prompt_name == "yes_no":
# do prompt
if prompt_name == "yes_no_ignore_case":
# do prompt
I realize that it's probably just a good idea to settle on one accepted "y/n" format for all of my programs, and I will. This is just for the sake of showing that, in cases where I would need a very similar but slightly different prompt, it would result in a lot of copy/pasted code (no flexibility with this method at all).
What is a good approach to writing clean, flexible, and easy-to-maintain user prompts?
(I've seen this: Asking the user for input until they give a valid response and some other responses. My question is not about how to get input and validate it, it's about how to make a flexible input system that can be reused with across multiple programs).
I once wrote a function for something similar. The explanation is in the doc-string:
def xory(question = "", setx = ["yes"], sety = ["no"], setz = [], strict = False):
"""xory([question][, setx][, sety][, setz][, strict]) -> string
Asks question. If the answer is equal to one of the elements in setx,
returns True. If the answer is equal to one of the elements in sety,
returns False. If the answer is equal to one of the elements in setz,
returns the element in setz that answer is equal to. If the answer is
not in any of the sets, reasks the question. Strict controls whether
the answer is case-sensitive. If show is True, an indication of the
acceptable answers will be displayed next to the prompt."""
if isinstance(setx, str):
setx = [setx]
if isinstance(sety, str):
sety = [sety]
if isinstance(setz, str):
setz = [setz]
if (setx[0])[0] != (sety[0])[0]:
setx = [(setx[0])[0]] + setx
sety = [(sety[0])[0]] + sety
question = question.strip(" ") + " "
while True:
if show:
shows = "[%s/%s] " % (setx[0], sety[0])
else:
shows = ""
user_input = raw_input(question + shows)
for y in [setx, sety, setz]:
for x in y:
if (user_input == x) or ((not strict) and (user_input.lower() == x.lower())):
if y is setx:
return True
elif y is sety:
return False
else:
return x
question = ""
show = True
Examples:
>>> response = xory("1 or 0?", ["1", "one", "uno"], ["0", "zero", "null"], ["quit", "exit"])
1 or 0? x
[1/0] eante
[1/0] uno
>>> print(response)
True
>>> response = xory("Is that so?")
Is that so? Who knows?
[y/n] no
>>> print(response)
False
>>> response = xory("Will you do it?", setz=["quit", "exit", "restart"])
Will you do it? hm
[y/n] quit
>>> print(response)
quit
I'd advise to write a library that contains a number of very clearly defined building blocks, each one as small and light-weight as possible, without too many assumptions baked in about how you're going to put the pieces together.
That is, I'd include one function that does the loop, but instead of passing in a set of rules, I'd allow for exactly one function to be passed in that either returns a value (if a valid input was given and after converting it in any way necessary) or raises a ValueError if the input wasn't usable. Other building blocks would implement certain checks or transformations (like resolution of 'y' and 'n' into boolean values).
This way, you would leave it completely up to the user to assemble the stuff in a way suitable for the use case.
# library:
def prompt(prompt, default, postprocess):
value = input('{} ({}): '.format(prompt, default)) or default
try:
return postprocess(value)
except ValueError:
continue
def check_lower(value):
if not value.islower():
raise ValueError()
def to_bool(value):
return value in 'yes'
# using the library:
def postprocess(value):
check_lower(value)
return to_bool(value)
prompt('Really?', 'n', postprocess)
I would create a prompt function as such:
def prompt(prompt, default=None, rules=[]):
while True:
response = input(prompt)
if response:
valid = [rule(response) for rule in rules]
if not(False in valid):
return response
else:
print('Invalid input')
else:
return default
You could then create different validation functions such as
def filterValidEmail(string):
if '#' in string:
if '.' in string.split('#')[1]:
return True
else:
return False
else:
return False
And call these functions like so:
prompt('What is your email? ', rules=[filterValidEmail])
You could also tweak this so that you can tell the user what verification they failed or disallow blank inputs.
The task is to get a user to input a password then, using recursion, make sure it has no vowels in it. If it does then let the user re-enter the password. This is what i have so far:
def passwordCheck(pwd):
"""checks if pwd has any vowels in it."""#doc string
vowels = 'aeiou'#specifies the characters that aren't allowed
if pwd == '':
return 0
elif pwd == None:
return None#Shouldn't be necessary but just in case
elif pwd[0] not in vowels:#checks that the 1st(0th) character is not a vowel
return passwordCheck(pwd[1:])#gets rid of the 1st(0th) character and starts again
elif pwd[0] in vowels:#checks if the 1st(0th) character is a vowel
return 1#if it is, stops the function calls and returns a value
password = str(input('Please enter a password with no vowels in it: '))#asks user to input their new password
x = passwordCheck(password)#checks the password is valid, i.e. no vowels
while x == 1:#when the password entered contains a vowel
print('\nSorry, that is not a valid password.\nYour password cannot contain any vowels.')#tells the user why their password is invalid
password = str(input('\nPlease enter a different password: '))#gives the user a chance to re-enter their password
x = passwordCheck(password)#checks to make sure the new password is valid
print('\nCongratulations, you have entered a valid password!')#tells the user if their desired password is valid
print('\nYou are now able to log on to the system with these credentials.')#could've been included on the previous line but looks neater here
I know this is probably not the most pythonic way of doing it but it works for me in most cases. I'd love to hear a better way but ideally someone can help in the same style. I don't want to just copy someones code without understanding it.
The question i have is dealing with the case where the user enters no password at all. The first if statement:
if pwd == '':
return 0
I thought it just dealt with the case when the string had been fully recursed through, i.e. no vowels, but after a minutes inspection it's obvious this applies to no password as well.
I had also tried using:
if pwd == None:
return something
Now i'm thinking the problem could be because i said:
password = str(input('######'))
but i've fiddled with that as well and still can't can't seem to make that work either! I've tried google and searching stackoverflow but no luck so if anyone has any ideas/solution they think might be helpful I'd be very grateful to hear them. Thank you very much.
My main question is:
How can i differentiate between a string that's empty because it's been recursed through and the user inputting nothing?
Solved.
ended up using
def passwordValid(pwd):
if len(pwd)>0 and passwordCheck(pwd)==0:
return pwd
else: return 'Fail'
password = str(input('Please enter a password with no vowels in it: '))#asks user to input their new password
y = passwordValid(password)#checks the password is valid, i.e. no vowels
while y == 'Fail':#when the password entered contains a vowel
print('\nSorry, that is not a valid password.\nYour password cannot contain any vowels or be empty.')#tells the user why their password is invalid
password = str(input('\nPlease enter a different password: '))#gives the user a chance to re-enter their password
y = passwordValid(password)#checks to make sure the new password is valid
print('\nCongratulations, you have entered a valid password!')#tells the user if their desired password is valid
print('\nYou are now able to log on to the system with these credentials.')#could've been included on the previous line but looks neater here
Thank you Wayne Werner for fixing the title and the main question.
This problem can be broken down into (at least) three distinct subproblems:
check whether a string contains vowels
check whether a string is a valid password (length > X and has vowels)
get a password from the user
Your code should reflect this structure. You could therefore use the following function layout:
def has_vowels(string):
if not string: # check for empty string
return False # empty strings never have vowels
# TODO we have a non-empty string at this point and can use recursion
def is_valid_password(string):
return len(string) > 0 and not has_vowels(string)
def request_password():
while True: # use an endless loop here, we don't won't to repeat
# the "input" statement. We could also change this to
# something like `for i in range(3)` to allow only a limited
# number of tries.
passwd = input('Please enter a password with no vowels in it: ')
# TODO check if input is valid, if yes, return, if no, print an error
Don't attempt to solve both problems with a single method. You have two ditinct critera: no vowels; minimum length.
def isPasswordValid(pwd):
return len(pwd) > 4 and not passwordCheck(password)
x = isPasswordValid(password)
...
You could solve this with recursion by adding another parameter which indicates how many characters have been looped through, but that is clumsy and offers no real benefit.
You can't differentiate between an empty string and an empty string. You can however set the variable to None, or to a string like "__no_string_entered_yet". That said, I don't see why you would need to, see the other answers.
I believe this does what your question asks for:
Not allow an empty password (different than a "blank" password?)
Not allow vowels in the password
I opted not use if/elif/else in favor of structuring it so that valid characters "fall through"
def pwd_check(s):
vowels = 'aeiou'
if len(s) == 0: return False # This is only valid in the first iteration
if s[0] in vowels: return False
if len(s) == 1: return True # Success: a 1 character pwd with no vowels
return pwd_check(s[1:])
I thought about putting checks in to make sure that a string like ' ' was not passed in, but I didn't see that explicitly asked for. pwd_check(password.strip()) solves this problem.
Here's how I like to do.
For the fun, I added conditions of minimum and maximum lengths for the password:
def passwordCheck(pwd,vowels = 'aeiou',minimum=5,maximum=12):
if pwd == '':
return 0,None,None
elif pwd[0] in vowels:
return -1,None,None
else:
y = passwordCheck(pwd[1:])[0]
if y==-1:
return -1,None,None
else:
return y + 1,minimum,maximum
mess = 'Please enter a password with no vowels in it: '
while True:
x,miin,maax = passwordCheckstr(input(mess))
if x==-1:
mess = ('\nSorry, that is not a valid password.\n'
'Your password cannot contain any vowels.\n'
'Please enter a different password: ')
elif x==0:
mess = ('\nSorry, you must enter a password.\n'
'Please do enter a password: ')
elif x<miin:
mess = ('\nSorry, the password must have at least %d characters.\n'
'The string you entered has %d characters.\n'
'Please, enter a new longer password: ' % (miin,x))
elif x>maax:
mess = ('\nSorry, the password must have at most %d characters.\n'
'The string you entered has %d characters.\n'
'Please, enter a new shorter password: ' % (maax,x))
else:
print ('\nCongratulations, you have entered a valid password!\n'
'\nYou are now able to log on to the system with these '
'credentials.')
break
edit
Another kind of algorithm.
I wasn't satisfied to return such tuple as -1,None,None
def check_password(forbidden,minimum,maximum):
def passwordCheck(pwd,cnt=0,forbid = forbidden,
miin=minimum,maax = maximum):
# cnt is the number of preceding turns of recursion
# that have been already executed.
if pwd == '':
if cnt==0:
# cnt==0 means that it's the first turn of recursion
# since pwd is '', it means no entry has been done
return 0
elif cnt<miin:
return -3
elif cnt>maax:
return -2
elif pwd[0] in forbid:
return -1
else:
if cnt in (-3,-2,-1):
return cnt
else:
return passwordCheck( pwd[1:] , cnt+1 )
mess = 'Please enter a password with no vowels in it: '
while True:
x = str(raw_input(mess)).strip()
y = passwordCheck(x)
if y==0: # inexistent string
mess = ('\nSorry, you must enter a password.\n'
'Please do enter a password: ')
elif y==-1: # string contains a vowel
mess = ('\nSorry, that is not a valid password.\n'
'Your password cannot contain any vowels.\n'
'Please enter a different password: ')
elif y==-2: # string too long
mess = ('\nSorry, the password must have at most %d characters.\n'
'The string you entered has %d characters.\n'
'Please, enter a new shorter password: ' % (maximum,len(x)))
elif y==-3: # string too short
mess = ('\nSorry, the password must have at least %d characters.\n'
'The string you entered has %d characters.\n'
'Please, enter a new longer password: ' % (minimum,len(x)))
else: # success
print ('\nCongratulations, you have entered a valid password!\n'
'You are now able to log on to the system with these credentials.')
break
# EXECUTION
check_password('aeiou',5,12)