Check for special characters in Python while loop

Question:

Below is code for a password generator which I’ve written using Python3 and tkinter. I’m having difficulty with the last line of code and any(c in spec for c in password). The while loop does not terminate when I add this last line to the if statement in the while loop. I’ve looked at all the other answers online for "check string for special characters" and can’t find a solution to make my while loop work. The last line of my code should check if there is any special characters in the password, if not, then generate password again.

import string
import secrets

alphabet = string.ascii_letters + string.digits + string.punctuation
spec = string.punctuation
while True:
    password = ''.join(secrets.choice(alphabet) for i in range(12))
    if (any(c.islower() for c in password)
        and any(c.isupper() for c in password)
        and any(c.isdigit() for c in password)
        and any(c in spec for c in password)):
        break
Asked By: codeblock

||

Answers:

You’re trying to generate a fully random password and if this doesn’t meet your criteria, start over.

This is an inefficient strategy, which can cause your loop to run many times (although here it shouldn’t be too bad for 1 character of each and 12 characters total).

You want 12 characters with at least 1 (or n) lower/upper/digit/special, so pick randomly as many of each as wanted and fill the rest with any character, then shuffle and join.

The secrets module does not have convenience functions such as choices/sample/shuffle of random, so the generation is a bit more lengthy.

Example with different constraints (at least 4 uppercase and 3 digits, 1 lowercase, 1 special)

# constraints
length = 12

pools = {'LC': string.ascii_lowercase,
         'UC': string.ascii_uppercase,
         'D' : string.digits,
         'P' : string.punctuation,
        }

min_num = {'LC': 1, 'UC': 4, 'D': 3, 'P': 1}

assert length >= sum(min_num.values())

alphabet = string.ascii_letters + string.digits + string.punctuation

# password generation
# step1: get random chars meeting the constraints
L = length
chars = []
for k,n in min_num.items():
    chars.extend(secrets.choice(pools[k]) for _ in range(n))
    L -= n

chars.extend(secrets.choice(alphabet) for _ in range(L))

# step2: shuffle the characters and join
# disclaimer: I don't know if this method is cryptographically secure
#             comments on that would be greatly appreciated
# see an alternative here: https://stackoverflow.com/q/66528995
shuffled_chars = []
for i in range(length, 0, -1):
    shuffled_chars.append(chars.pop(secrets.randbelow(i)))

password = ''.join(shuffled_chars)

print(password)

Example outputs:

1ENSY55DPp+N
9%O)5GF7ntKV
0&EQL2VV4M6f
Answered By: mozway

The code and any(c in spec for c in password) does in fact work. After PyxlDavon pointed out that my code does work, I closed and restarted my python interpreter and the code did work. So my only conclusion is that my python interpreter became unstable for some unkown reason. I haven’t seen this code style in any other related questions. I apologize for answering my own question. I got the idea for this code from this link: https://www.codespeedy.com/detect-if-a-string-contains-special-characters-or-not-in-python/#:~:text=To%20check%20if%20a%20string,special%20characters%20from%20the%20string.

Answered By: codeblock