Search code examples
pythonstringpassword-generator

[Random String Generator]Getting stuck in a loop on else condition


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!


Solution

  • 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.