Stijn Biemans
Stijn Biemans

Reputation: 1

How can I resolve maximum recursion depth error

I'm generating a random password with a desired length. I want it to have at least 2 uppercase letters, 2 lowercase letters, 2 digits and 2 special characters. I've tried multiple things, but every time I get this recursion depth error. Can anybody tell me what I've done wrong?

list_lower =['a','b','c','d','e','f','g','h','i','j','k','l','m','n','o','p','q','r','s','t','u','v','w','x','y','z']
list_upper = ['A','B','C','D','E','F','G','H','I','J','K','L','M','N', 'O','P','Q','R','S','T','U','V','W','X','Y','Z'] 
list_digit = [1,2,3,4,5,6,7,8,9,0]
def generatePassword(desiredLength: int) -> str:
    x = 0
    password = ""
    for x in range (desiredLength):
        password = password + chr(random.randint(33,126))
        list(password)
        list_password = list(password)
        times_lower = 0
        times_upper = 0
        times_digit = 0
        times_special = 0
        for character in list_password:
            if character in list_lower:
                times_lower += 1
            elif character in list_upper:
                times_upper += 1
            elif character in list_digit:
                times_digit += 1
            else:
                times_special +=1
        if times_lower >= 2 and times_upper >= 2 and times_digit >= 2 and times_special >= 2:
            return password
        else:
            return generatePassword(desiredLength)

generatePassword(7)

I get the error in line 30 which makes the function recursive.

Upvotes: 0

Views: 67

Answers (3)

Bradley Young
Bradley Young

Reputation: 11

If you are generating passwords, it's important that you generate ones that actually have enough randomness to not be predictable.

Random string generation with upper case letters and digits in Python

Has a great breakdown of how to generate a password that's truly random:

''.join(random.SystemRandom().choice(string.ascii_uppercase + string.digits) for _ in range(N))

(adding "special characters" and "lowercase characters" omitted to preserve the existing code)

I know that this is a somewhat oblique answer (i.e. it does not answer the question directly), so here's a potential solution if you still need the "it must contain these types of characters" (even though that would actually reduce security):

import random
import string
from collections import Counter

def gen(N):
    return ''.join(random.SystemRandom().choice(string.ascii_letters + string.digits + string.punctuation) for _ in range(N))

while True:
    pw = gen(8)
    counts = Counter(pw)
    upper = lower = digit = special = 0
    for (letter, count) in counts.items():
        if (letter in string.ascii_lowercase):
            lower += 1
        elif (letter in string.ascii_uppercase):
            upper += 1
        elif (letter in string.digits):
            digit += 1
        else:
            special += 1
            pass
    if (lower > 1 and upper > 1 and digit > 1 and special > 1):
        print("password is {}".format(pw))
        break
    print("failed password: {}".format(pw))

Upvotes: 0

bened
bened

Reputation: 11

times_digit will never be >= 2 because it tests stings (e.g. "2" against the integers in your list, (e.g. 2) change your list_digit to

list_digit = ["1","2","3","4","5","6","7","8","9","0"]

and try again.

By the way this could be done much simpler and doensn't need a recursive function.

Upvotes: 0

Caleth
Caleth

Reputation: 63152

Calling generatePassword(7) will never generate a password with 2 of each of 4 distinct categories.

You don't need recursion at all.

def generatePassword(desiredLength: int) -> str:
    while True:
        password = ""
        for x in range (desiredLength):
            password = password + chr(random.randint(33,126))
        times_lower = 0
        times_upper = 0
        times_digit = 0
        times_special = 0
        for character in password:
            if character in list_lower:
                times_lower += 1
            elif character in list_upper:
                times_upper += 1
            elif character in list_digit:
                times_digit += 1
            else:
                times_special +=1
        if times_lower >= 2 and times_upper >= 2 and times_digit >= 2 and times_special >= 2:
            return password
        else
            print ("Rejecting ", password)

That will loop forever if asked to generate a password of length 7 or less. We can improve that by checking the desired length first

if desiredLength < 8:
    raise ArgumentError("Cannot generate passwords shorter than 8 characters")

Upvotes: 1

Related Questions