0

I am new to Python programming but trying to make a password checker to that checks for certain things in a single string input. I want to restart the while loop if any method returns else.

def length():
    if len(password) > 8:
        print("Password is long enough. ")
    else:
        print("Password too short, please try a new password. ")


def numbers():
    if len([num for num in password if num.isdigit()]) >= 2:
        print("Password has two digits. ")
    else:
        print("Password needs two digits, please try a new password. ")



def lower():
    if any(map(str.islower, password)):
        print("Password has lower case. ")
    else:
        print("Password needs an lowercase letter, please try a new password.")



def upper():
    if any(map(str.isupper, password)):
        print("Password has upper case. ")
    else:
        print("Password needs an uppercase letter, please try a new password.")



while True:
    password = str(input("What is your password? \n"))
    length()
    numbers()
    lower()
    upper()

I don't know what I could do to efficient go through these methods and check if I get the result I want.

Tate
  • 1
  • See: https://stackoverflow.com/questions/7129285/what-is-the-purpose-of-the-return-statement-how-is-it-different-from-printing – slothrop Mar 30 '23 at 14:56
  • By the way, you don't need spaces at the end of your strings or before `\n`. Also: "an lowercase" -> "a lowercase" (see my code). – Lover of Structure Mar 30 '23 at 15:02

4 Answers4

1

You could do something like this:

from typing import List


def length(pwd: str) -> bool:
    return len(pwd) > 8


def numbers(pwd: str) -> bool:
    return len([num for num in pwd if num.isdigit()]) >= 2


def lower(pwd: str) -> bool:
    return any(map(str.islower, pwd))


def upper(pwd: str) -> bool:
    return any(map(str.isupper, pwd))


if __name__ == "__main__":
    functions_to_check: List = [length, numbers, lower, upper]
    is_password_satisfactory: bool = False
    password: str = ""
    while not is_password_satisfactory:
        password = str(input("What is your password? \n"))
        is_password_satisfactory = all(map(lambda f: f(password), functions_to_check))
    print(f"{password} is satisfactory")

Just a quick recap on how any and all work: they both take as argument a list of booleans, and return True if any (or all the) elements inside the list are True.
Expanding on that idea, we first list what are the functions we must use to verify our passwords (functions_to_check).
Then we will loop until the password is valid.
We check if the password is valid by building a list of booleans representing if the password passed each check (map(lambda f: f(password), functions_to_check): we apply each verification function to the password and store the result inside a list) and then we check whether all the elements of this list are True or not (we pass it as argument to the all function).
We then store the result of the all function into the variable we use to continue looping, and if the password is valid, we break.

GregoirePelegrin
  • 1,206
  • 2
  • 7
  • 23
  • Should it be `while not password_satisfactory:` ? – slothrop Mar 30 '23 at 14:57
  • I think the name was just confusing, I changed it to `is_password_satisfactory`. This value is `False` if any of the checks is False, so putting `while not is_password_satisfactory` would be to say that every password except those satisfying the conditions are acceptable. – GregoirePelegrin Mar 30 '23 at 15:03
  • But `while cond` means we will keep asking for a password **until** `cond` is true. We stop asking when `cond` is False. So `cond` should express the condition that the password is **not** acceptable. – slothrop Mar 30 '23 at 15:06
  • As it stands, the `while` loop will never run, because `is_password_satisfactory` is initialised to False. Have you run this code to test it? – slothrop Mar 30 '23 at 15:07
  • Ah, you're absolutely right! Absolutely sorry for my earlier answer! I tested it and it ran well, until I tried refactoring while writing my answer... – GregoirePelegrin Mar 30 '23 at 15:09
  • 1
    Haha, can't claim I've never done that myself :) – slothrop Mar 30 '23 at 15:10
0

You can return a boolean value from your function that acts as a flag whether the while loop should continue or not.

If the password has not any uppercase letter the function return True so the loop keeps calling upper().

I encourage you to try to find how to implement it for all functions.

def upper():
    if any(map(str.isupper, password)):
        print("Password has upper case. ")
        return False #exit
    else:
        print("Password needs an uppercase letter, please try a new password.")
        return True # keeps going

flag = True
while flag:
    password = str(input("What is your password? \n"))
    flag = upper()
Jordi Bustos
  • 108
  • 7
0
  1. Add arguments to your functions.
  2. Have your functions return bool values.
  3. Use break to break out of the while-loop if all conditions are met.
def length(password):
    if len(password) > 8:
        print("Password is long enough.")
        return True
    else:
        print("Password too short, please try a new password.")
        return False

def numbers(password):
    if len([num for num in password if num.isdigit()]) >= 2:
        print("Password has two digits.")
        return True
    else:
        print("Password needs two digits, please try a new password.")
        return False

def lower(password):
    if any(map(str.islower, password)):
        print("Password has lower case.")
        return True
    else:
        print("Password needs a lowercase letter, please try a new password.")
        return False

def upper(password):
    if any(map(str.isupper, password)):
        print("Password has upper case.")
        return True
    else:
        print("Password needs an uppercase letter, please try a new password.")
        return False

while True:
    pw = str(input("What is your password?\n"))
    if (length(pw) and numbers(pw) and lower(pw) and upper(pw)):
        break
Lover of Structure
  • 1,561
  • 3
  • 11
  • 27
0

An alternative to returning Booleans is using exceptions:

class InvalidPasswordException(Exception):
    pass


def validate_length(password: str):
    if len(password) < 9:
        raise InvalidPasswordException("Password too short, please try a new password.")


def validate_numbers(password: str):
    if len([num for num in password if num.isdigit()]) < 2:
        raise InvalidPasswordException("Password needs two digits, please try a new password.")


def validate_lower(password: str):
    if not any(map(str.islower, password)):
        raise InvalidPasswordException("Password needs an lowercase letter, please try a new password.")


def validate_upper(password: str):
    if not any(map(str.isupper, password)):
        raise InvalidPasswordException("Password needs an uppercase letter, please try a new password.")


def get_password() -> str:
    while True:
        try:
            password = str(input("What is your password? \n"))
            validate_length(password)
            validate_numbers(password)
            validate_lower(password)
            validate_upper(password)
            return password
        except InvalidPasswordException as error:
            print(error)


if __name__ == '__main__':
    get_password()

Also, you probably want to break out of your while True loop. For this you can use either break or return (if it is inside a function).

Using exceptions is neither better nor worse than the other approach. It is more of a different style (see also "ask for forgiveness not permission").

Baumkuchen
  • 145
  • 8