Write a function that checks whether a string is valid password.
Rules:
Must have at least 8 characters
A password must consist of only letters and digits
A password must contain at least 2 digits
Heres what I have so far, what am i doing wrong? thank you
def getPassword():
password = input("Enter password: ")
return password
def validPassword(password):
if len(password) >= 8:
valid = True
if password.alnum():
valid = True
if password.isdigit < 2:
valid = True
else:
return False
def main():
password = validPassword()
if validPassword(password):
print(password, "is valid")
else:
print(password, "is invalid")
main()
This seems like a homework assignment so I'll try and stay from directly answering, but more try to push you in the right direction.
Your first bit of code that will run will be
...
def main():
password = validPassword() # the password we are trying to check
...
Uh oh, validPassword(password) takes an argument and doesn't get a password, maybe you meant getPassword()
Lets go through the logic of validPassword(password) Line by line
...
def validPassword(password):
if len(password) >= 8:
valid = True
...
lets check if the length of the string is more than 8 characters, if it is, we initialize the variable valid and set it to True
...
if password.alnum():
valid = True
...
Then regardless of what has happened, we call alnum, ( which I don't think is a function in python, probably meant isalnum. ) to check if all the characters in the password are numbers.
If it is we initialize the variable valid and set it to True.
You may say, but I already initialized it, well not really, in python there is scope.
...
if password.isdigit < 2:
valid = True
...
Then we check if the passwords method isdigt is less than 2, maybe you meant password.isdigit() I am really being meticulous as it is unclear your proficiency in programming or python. But if you meant password.isdigit() < 2 then you are asking if the password is a digit and if yes, is it less than 2.
If it is we initialize the variable valid and set it to True.
...
else:
return False
...
Then if and only if password.isdigit() < 2 is false, we return false.
Here are some pointers:
Learn about control flow in python
When you ask a question here, rather than say, "here is problem, here
is my current code, please help," say, "here is problem, here is my
current code, this is what I expect on line x,y,z and a,b,c is
happening, please help" if we do not know where you are struggling,
how can we help you best
Try and run your code and show us the stacktrace ( if it exists ), there are definitely errors here that would have come up, python happens to have nicer errors that most languages (in my opinion)
Hopefully my line by line explanation has helped you find some of your errors and a better idea of how to continue, if not, feel free to amend your question so that we get a better idea of how to help.
Happy coding.
According to the following reference here for the isdigit() method :
This method returns true if all characters in the string are digits
and there is at least one character, false otherwise.
Which doesn't hold for your case
A password must contain at least 2 digits
The method will only let you know if the given string is a digit, not how many digits are in a string. To achieve that you will need to play around a bit.
You may use the following
if sum(character.isdigit() for character in password) >= 2:
Furthermore, your code has a small mistake as you will never return back True. Here is a possible fix:
def CountDigitsFor(password):
return sum(character.isdigit() for character in password)
def validPassword(password):
if len(password) >= 8 and password.isalnum() and CountDigitsFor(password) >= 2:
return True
return False
Additioanly, in your main you have a small typo when getting the password from the user
password = validPassword()
Should be
password = getPassword()
Therefore here is a complete code
def getPassword():
return input("Enter password: ")
def CountDigitsFor(password):
return sum(character.isdigit() for character in password)
def validPassword(password):
if len(password) >= 8 and password.isalnum() and CountDigitsFor(password) >= 2:
return True
return False
def main():
password = getPassword()
if validPassword(password):
print(password + " is valid")
else:
print(password + " is invalid")
main()
Related
I am just getting into programming recently and still many things are not clear to me. However in this code I just don't understand the logic. I premise that I don't want to use either import or def(), but I want to understand how I have to reason to make this piece of code work.
A User enters the pwd, and I want there to be a check for numbers and letters that must be present.
The first while is to avoid empty pwd; at this point I dismember the pwd and check for letters and numbers.
Enter the second while and here if I enter only numbers or only letters it works but if I put numbers and letters it keeps staying in the loop.
I tried putting a break (is commented) but it doesn't work.
If anyone can make me understand what I am doing wrong I will be grateful.
Regards
pwd = input("Enter password.\n")
# This loop is used to check if the User has not typed in the password.
while len(pwd) == 0: # Enter the loop when it finds the pwd box empty
print("You left the password box empty.\n")
pwd = input("Enter the password.\n") # Query the password
n = ''.join(filter(str.isdigit, pwd))
char = pwd.isalpha()
num = n.isdigit()
# print(char, num)
# Control loop so that there are letters and numbers
while (char== False and num == True) or (char== True and num == False):
n = ''.join(filter(str.isdigit, pwd))
char= pwd.isalpha()
num = n.isdigit()
print("Attention the password must contain numbers and letters.")
pwd = input("Enter password again.\n") # Prompt for password
# break
if (char== True and num == True):
break
print("Password contains numbers and letters. Accepted!")
PS: Instead of While I tried using if, but if the User keeps putting either only numbers or only letters I have no way to go back as I can with While.
First, Issue you are getting is because of the line
char = pwd.isalpha()
At this point char will always be False, unless pwd is only alpha. So, when you enter a password that is mix of alpha and number example 123asd, char will always be False as you are directly using isalpha() method on pwd. What you want to do here is use same approach you used to check numbers. That mean adding the line below,
c = ''.join(filter(str.isalpha, pwd))
before
char = c.isalpha()
This way you are filtering all the alpha out.
Second, inside your 2nd while loop. You need to move the lines
print("Attention the password must contain numbers and letters.")
pwd = input("Enter password again.\n") # Prompt for password
on top, before doing all those alpha and digit filters. So, that you will always have the latest value stored in the pwd.
[OPTIONAL]
Furthermore, using your same approach, I would suggest to refactor your code as following.
pwd = input("Enter password.\n")
num = ''.join(filter(str.isdigit, pwd))
char = ''.join(filter(str.isalpha, pwd))
while not pwd or not num or not char:
if not pwd:
print("You left the password box empty.")
else:
print("Attention the password must contain numbers and letters.")
pwd = input("Enter password again.\n")
num = ''.join(filter(str.isdigit, pwd))
char = ''.join(filter(str.isalpha, pwd))
print("Password contains numbers and letters. Accepted!")
So (as you will probably see from my code) I am a beginner at Python (version 3.8.3) and enjoying it very much so far, and I have challenged myself on several different beginner projects. I am currently making a random string generator (i.e. a password generator, hence the use of the secrets module).
# Password Generator
import secrets, string
print("Welcome to the generator. Please specify your requirements")
print("A. All Characters;\nB. No Numbers;\nC. No Punctuation\nPlease choose the appropriate letter for your needs.")
userInput = input()
def userWelcome():
if userInput.lower() == "a":
generatePass = string.ascii_letters + string.digits + string.punctuation
print("How long do you want your string to be?")
stringRange = int(input())
print( "".join(secrets.choice(generatePass) for _ in range(stringRange)) )
elif userInput.lower() == "b":
generatePass = string.ascii_letters + string.punctuation
print("How long do you want your string to be?")
stringRange = int(input())
print("".join(secrets.choice(generatePass) for _ in range(stringRange)))
elif userInput.lower() == "c":
generatePass = string.ascii_letters + string.digits
print("How long do you want your string to be?")
stringRange = int(input())
print("".join(secrets.choice(generatePass) for _ in range(stringRange)))
else:
print("Not an option! Let's try again.")
userWelcome()
userWelcome()
However, my problem is what to do if the user inputs an incorrect option. As you can see, with the else statement I assume what they filled in does not match any of the earlier options - and so I want to try to rerun the generator again (so I try to call userWelcome again in the else statement).
However, when I type in for example 12 as input, my shell starts to output my string (Not an option Let's try again) literally a thousand times like it is stuck in a loop. I am wondering what I am doing wrong exactly.
What I have tried:
(1) So I have tried to solve this input problem first with try and except, running the except when there is a ValueError but that only works for numbers and I did not manage to rerun userWelcome()
(2) I have tried to create a elif statement in which I check the input for integers, however that also gets stuck in a loop. Code:
elif userInput.isalpha() == False:
print("Not an option! Let's try again.")
userWelcome()
Anyway, I hope that explains it well. I have been busy with this for a few hours now and I thought I'd ask this. Maybe it's a very stupid question but for me it's hard :)
TL;DR: Want to check for proper user input by running my function again, get stuck in weird loop
Thank you for your time and effort!
The code calls userWelcome() recursively, without changing the global variable userInput. The same bad string is processed again, causing the same result, which again calls userWelcome() - for ever (at least until max call depth).
You should read a new string at the beginning of userWelcome, instead of using a global variable. Also, recursion here is an overkill that confuses you. Better use a simple while loop:
while True:
userInput = ....
if ....
do something
return
elif ...
do something else
return # exit the function - breaking out of the loop
else:
print(error message)
# No return here, means the loop will continue to loop
If you want to call the function instead of loop inside, you can instead make the function return success (True) vs. failure (False), and loop that in the caller:
while not userWelcome(inputString):
inputString = read the string
def userWelcome(inputString):
if inputString == ....:
something
return True # To mark OK
elif inputString == .....:
something else
return True # To mark OK
else:
print an error
return False # To mark failure
Just avoid global variables, it is a bad practice. Pass the value through parameters, as in the code above.
When I run the program everything is fine until the password validity function is ran and all the conditions of length and uppercase and lowercase are equal to true. What the program is supposed to do here is run the creditcard function but it doesn't as seen here:
if length and upper and lower == True:
creditcard()
It doesn't give me any errors or syntax messages about it when the conditions of length, upper, and lower case are all true but still will not run the creditcard function, the program simply ends. however when any of the conditions of length uppercase or lowercase are not met using or logic then "The password's invalid" is still successfully printed.
def creditcard():
balence = input("What is your credit card balence this month")
choice = input("Would you like to make a payment or make the standard deduction? y or n")
intrest = .1596/12
amountdue = balence*interest
enter code here
if choice == "y":
payment = input("How much would you like to pay?")
if payment == "n":
if 0.3 * amountdue>25:
payment = 0.3 * amountdue
elif 0.3 * amountdue<25:
payment = 25
balence-=payment
print("Your balence is now", balence)
def validity(password):
length=()
upper=()
lower=()
if len(password)<5:
length = True
else: lenghth = False
for char in password:
if char.isupper():
upper = True
else: upper = False
for char in password:
if char.islower():
lower = True
else: lower = False
if length and upper and lower == True:
creditcard()
if length or upper or lower == False:
print("The password's invalid")
password=""
def main():
password=""
while password=="":
password= input("Please enter a valid password")
validity(password)`enter code here`
main()
The problem is that your if condition is never being met. You can check this quickly by putting something like print("Going to call creditcard") between the if and creditcard() lines. You'll see the message is never printed. To find out why, you could print the values of length, upper and lower before the ifs to see if the values are what you expect.
That's how I'd recommend trying to diagnose your problem with quick and dirty debugging. As for explaining what your problem actually is:
When you do for char in password this is going through each character in the password and then running the test for is upper/lower for that character. This means if you have the password aB then on the first run it will check a and so will set lower to True, but then it will test the next character, B, and set lower to False. So basically, it's only checking the case of the last character, and as a single character can't be both upper and lower your condition for upper and lower being true will never be met.
A better way to do it is to initialise all the conditions as being false and then set them to true when met, e.g.
upper = False
for char in password:
if char.isupper():
upper = True
Edit: As deceze pointed out, you also need to be careful with the line if length or upper or lower == False. Because that is not the same as if (length == False) or (upper == False) or (lower == False). Instead, it evaluates it like: if length or upper or (lower == False), and an or operator returns true if either condition is true.
As an example, if the length = False, upper = False and lower = True, the condition would then be: if False or False or (True == False), which gets evaluated to False or False or False which gets evaluated to False. This may be a bit confusing, check the link deceze commented for a fuller description, but in your case a simpler way would be to just use an else.
There are a few more issues I can see, I realise you may not have got to this stage of checking your code yet, but just a heads up:
There are a few typos which can trip you up if you spell a variable differently throughout your code.
I assume that you want to length to be greater than 5? Currently you're using less than.
When you use input() the value is always a string (text) this will cause problems when you try to do your calculations with balance and payment. It's easy to overcome by telling Python to cast the data to a number by using int() for whole numbers, or float() for decimals, like so: balance = int(input("What is your credit card balance this month?"))
And finally (of the things that I can see), it looks like you're trying to make a loop so the user can keep re-entering the password if their previous attempt was invalid? But the condition on the while loop is just checking if the password is empty, so will terminate as soon as the user types in any password. You could create a new boolean called created_password initialised to False and use that as the condition? If I were you I'd do this by making validity just return True or False (depending on if the password is or isn't valid), then call creditcard() from main once out of the while loop.
I'd recommend you try to work through the errors yourself, this way you'll be able to actually see the error messages and learn how to fix them. If you get stuck I've made a version which works here: https://trinket.io/python3/b6b8c0ca31 which you can compare with your code.
Or a slightly more compact version: https://trinket.io/python3/d934a3a48e
This question already has an answer here:
Python recursion with list returns None [duplicate]
(1 answer)
Closed 5 years ago.
I'm creating a password generation function for an exercise where I am checking if the user is specifying at least 8 symbols length for the password the be generated while wanting to check if there are 3 wrong input attempts I would just exit the program using return.
I was expecting the return function to STOP everything and not proceed with the following loop afterwards, but that was not the case.
Can you please help me understand why it's happening as it is?
here is the code:
import string
import random
attempts = 0
def PasswordGenerator(passwordlenght):
passwordlenght = int(passwordlenght)
password= ""
i = 0
if passwordlenght < 8:
print("Password length must be more than 8 symbols !!!")
global attempts
attempts += 1
if attempts <3:
PasswordGenerator(passwordlenght)
else:
return 1
while i < passwordlenght:
if i in range(1,passwordlenght,3):
password += string.ascii_lowercase[random.randrange(len(string.ascii_lowercase))]
elif i in range(2, passwordlenght, 3):
password += string.ascii_uppercase[random.randrange(len(string.ascii_uppercase))]
elif i in range(3, passwordlenght, 3):
password += string.digits[random.randrange(len(string.digits))]
i += 1
print(password)
PasswordGenerator(5)
What is happening, is that it goes into the 'invalid' section, calls PasswordGenerator again, then finishes the rest of the code. So on the 3rd attempt it will return, NOT generating a password, but will then finish the 2nd attempt, and generate a password, then finish the 1st attempt and generate a password. There are a few ways you can do this, make the while loop at the end an 'else' condition to if passwordlength < 8, or remove the return from the 'else' of if attempts < 3. The following demonstrates both techniques (though you only need 1)
import string
import random
attempts = 0
def PasswordGenerator(passwordlenght):
passwordlenght = int(passwordlenght)
password= ""
i = 0
if passwordlenght < 8:
print("Password length must be more than 8 symbols !!!")
global attempts
attempts += 1
if attempts <3:
PasswordGenerator(passwordlenght)
return 1
else:
while i < passwordlenght:
if i in range(1,passwordlenght,3):
password += str ing.ascii_lowercase[random.randrange(len(string.ascii_lowercase))]
elif i in range(2, passwordlenght, 3):
password += string.ascii_uppercase[random.randrange(len(string.ascii_uppercase))]
elif i in range(3, passwordlenght, 3):
password += string.digits[random.randrange(len(string.digits))]
i += 1
print(password)
PasswordGenerator(5)
Edit: I am assuming that the way it is currently coded is for testing purposes, otherwise you will only ever pass the originally provided value as the password length, and it will always fail (if less than 8). If that is not what you are doing, you should change that as well.
I suspect your problem is with this line:
PasswordGenerator(passwordlenght)
Your function is recursively calling itself, but since it doesn't return the value returned by the recursive call, it will go on to run the rest of the code afterwards.
You could "fix" it by adding a return:
return PasswordGenerator(passwordlenght)
But that's still not a very good solution. For most programs that need to do something a certain number of times, it's much better to use a loop, rather than recursion. Try something like this, which will check three times that indeed the same length is indeed too short:
for attempt in range(3):
if passwordlenght < 8:
print("Password length must be more than 8 symbols !!!")
else:
# put the rest of the code here, including the return
Obviously this is a bit silly, since if the length is too short the first time, it will still be too short the second and third times too. I'm not really sure what the point of multiple attempts is for this particular check (it could perhaps make sense for some other checks on a candidate password, but not this one, since the length is provided by the user rather than being random).
I'm a newbie writing hangman and have hit a bug in one of my modules. My intent in this module is to check the user input and verify it is a single character that is a letter and not a number. The error checking works in that it won't exit the module until a single letter ( or special, haven't figured a way around that yet) is entered but the return value is always the first user input entered not the last and correct entry. Any suggestions would be appreciated.
def get_guess():
guess = str(raw_input('Please enter your guess letter: '))
if len(guess) == 1:
try:
float(guess)
is_int = True
except ValueError:
is_int = False
if is_int:
print "You have entered a number not a letter."
get_guess()
else:
print "Please enter a single letter."
get_guess()
return guess
You are using recursion to get repeated inputs, but are not returning the recursive call results. You do need to return whatever the recursive call produced to pass it back up the stack:
return get_guess()
You'll need to do this in both locations you are calling get_guess() recursively.
Using recursion to get a response is not a good idea however; never underestimate the determination of idiots to get it wrong and instead hit your recursion limit. Use a loop instead:
def get_guess():
while True:
guess = raw_input('Please enter your guess letter: ')
if len(guess) == 1:
if guess.isdigit():
print "You have entered a number not a letter."
else:
return guess
else:
print "Please enter a single letter."
Here the function keeps looping endlessly until you return the one valid character guess. Note that you probably want to test for str.isalpha() instead if only letters are permitted. Your code and my version allow for anything that is not a digit, so spaces and punctuation are allowed too.
You may also want to study Asking the user for input until they give a valid response