0

I am coding a password checker for an assignment. Here are the requirements:

At least 2 letter between [a-z] and 1 letter between [A-Z]. At least 1 number between [0-9]. At least 1 character from [?$#@]. Minimum length 10 characters. Maximum length 14 characters. Moreover, in case of invalid password, the program should keep on asking for new password unless it validates it successfully.

For some odd reason, my code ignores the while loop conditions and jumps out of it after completing the for loop. What am I doing wrong and how could I fix this? Any help is appreciated!

import string

#password
password = input("Enter a password that has at least two lower case letter, one upper case letter, one number between one and nine, and one of the following characters: '?$#@.'")


#requirements
ll = list(string.ascii_lowercase)
ul = list(string.ascii_uppercase)
nums = ["1","2","3","4","5","6","7","8","9"]
char = ["?","$","#","@"]

#counters
llcounter = 0
ulcounter = 0
numscounter = 0
charcounter = 0

while llcounter < 2 or ulcounter < 1 or numscounter < 1 or charcounter < 1:    
    if len(password) > 10 and len(password) < 14:
        for x in password:
            if x in ll:
                llcounter += 1
            elif x in ul:
                ulcounter += 1
            elif x in nums:
                numscounter += 1
            elif x in char:
                charcounter += 1
    else:
        password = input("Your password has not met the requirements, please try another one.")
  • 1
    You don't say what input you're giving your program, but I note that you don't re-prompt for a new password if it's not strong enough (only if it has the wrong length), and you don't reset the counters when the user enters a new password. – Samwise Mar 12 '21 at 00:52
  • 1
    My suggestion would be to write a function whose only job is to validate one password, put all the counters and length checks inside that function, and then call that function in your loop. Encapsulating all that logic inside a function makes it impossible for you to have the type of bug you've got here where data from one password is leaking into the validation check for another. – Samwise Mar 12 '21 at 00:54
  • Put password length check inside `while` conditions otherwise the loop may exit when the password is 2+1+1+1=5 characters long. Put the `input` from the start of the file inside the `while` loop, i.e. assume that initially password is wrong (as you don't have one) and then keep asking until all conditions are fulfilled. – NotAName Mar 12 '21 at 00:58
  • Please supply the expected [minimal, reproducible example](https://stackoverflow.com/help/minimal-reproducible-example) (MRE). We should be able to copy and paste a contiguous block of your code, execute that file, and reproduce your problem along with tracing output for the problem points. This lets us test our suggestions against your test data and desired output. Show where the intermediate results differ from what you expected. – Prune Mar 12 '21 at 01:05
  • 1
    Your posted code hangs waiting for input -- don't expect us to enter test data, or to build a test file. Instead, simply hard-code a test case that causes the problem. – Prune Mar 12 '21 at 01:05
  • Think about it this way. Fetch the string. While forever, count up the character classes. If the numbers are good, break out the loop Otherwise, prompt again. Remember to clear the counters INSIDE the while loop. – Tim Roberts Mar 12 '21 at 01:10
  • @TimRoberts: but it's best practice to delegate all the checking to a function `is_valid(pwd)` which returns bool. Then it will be impossible to have the counter logic outside that function; also every time it's called it will initialize its counters. – smci Mar 12 '21 at 01:16

4 Answers4

0

well that is because your nested loop logic is flawed. it will keep iterating through the for loop and never checks the while loop conditions. you want the for loop iteration on the outside and break outside when the while conditions are met

for ...:
    while ...:
      ...
    break 

 
0
password = input( ... first prompt ... )
while True:
    set all counters = 0
    for x in password:
        if x in ll:
           ...etc, just like above...
    if llcounter >= 2 and ulcounter and numscounter and charcounter:
        break
    password = input ( ... second prompt ... )
Tim Roberts
  • 48,973
  • 4
  • 21
  • 30
0

You need to look up Validate input. You have interleaved steps of your logic. The basic structure is:

valid_pwd = False
while not valid_pwd:
    password = input("Enter a password ...")
    # Make *one* pass to validate the password.
    #   Count each type of character;
    ...
    #   Also check the length
    length_valid = 10 < len(password) < 14
    chars_valid = (
        ll_count >= 2 and
        lu_count >= 1 and 
        ...
    )
    valid = length_valid and chars_valid
    if not valid:
        print("Invalid password")

Does that get you going?

Prune
  • 76,765
  • 14
  • 60
  • 81
0

When coding in Python you should always try to find the most Pythonic way to implement your idea, namely by making the most of the language's potential to write concise, clear and functional code.

In your case you have redundant code in several places, especially that while loop with so many counters is pretty confusing.

Also, in order to maintain an active input session for the user, you should put the prompt inside the while loop, otherwise the password will always be the same entered for the first (and only) time making the loop itself rather pointless.

For example, here's how you might implement this password check. I commented on the code to try to explain what it's actually doing.

import string

ll = list(string.ascii_lowercase)
ul = list(string.ascii_uppercase)
nums = list(string.digits)[1:] # List of digits except 0
sc = ["?","$","#","@"]



# while True meaning keep prompting forever until password requisites are satisfied. 
# This can be changed with a counter to exit the loop after a certain number of attempts. 
while True: 
    password = input("Enter a password that has blablabla...") # Prompt the user at every iteration of the loop.

    # The following if block merges all your conditions in the same place avoiding hyper-nested 
    # conditions that are always confusing and kind of ugly. 
    # The conditions are based on list comprehension 
    # (see py docs, since its a super cool feature that is the ultimate tool for conciseness).
    if (len([char for char in password if char in ll]) >=2 and
        [char for char in password if char in ul] != [] and
        [char for char in password if char in nums] != [] and
        [char for char in password if char in sc] != []):
        print(f" Your new password is: {password}")
        break # break interrupts the endless loop as soon as a valid password is entered
    else:
        print("Your password has not met the requirements, please try another one.")
Max Shouman
  • 1,333
  • 1
  • 4
  • 11