
Senhas fora da política
Tive a sensação de que já escrevi uma função para gerar senhas cinco vezes. E ele fazia diferente sempre. E a razão para isso são os diferentes requisitos de senha para diferentes projetos e ferramentas. Não haverá código complexo, apenas um resumo de uma nova solução simples que veio a mim ontem.
Vamos começar com requisitos simples de senha:
- deve ter comprimento arbitrário
 - deve consistir em qualquer caractere impresso
 
import string
import random
from typing import List
def generate_password(length: int) -> str:
     """
     Generate a password of a given `length`.
     """
     result: List[str] = []
     choices = string.printable 
     while len(result) < length:
         symbol = random.choice(string.printable)
         result.append(symbol)
     return "".join(result)
Nós tentamos:
>>> generate_password(8)
... "1{k]/2)h"
>>> generate_password(13)
... "9ar|&:a+U]Il$"
Bem, a tarefa está concluída, podemos assistir fotos com gatos até o final do dia útil.
Política repentina
, , MyDB . , :
, . :
Ok, isso já é complicado, então vamos começar com uma função generate_random_stringque simplesmente gera seqüências aleatórias a partir do que elas deram.
import string
import random
from typing import List
def generate_random_string(length: int, *choices: str) -> str:
    """
    Generate a string of a given `length`.
    The result has at least one symbol from each of `choices` if `length` allows.
    Arguments:
        length -- Result string length.
        choices -- Strings with available symbols.
    """
    if not choices:
        
        choices = (string.ascii_letters, ) 
    
    all_choices = "".join(choices)
    result: List[str] = []
    choice_index = 0
    while len(result) < length:
        
        
        if choice_index < len(choices):
            symbol = random.choice(choices[choice_index])
            result.append(symbol)
            choice_index += 1
            continue
        
        symbol = random.choice(all_choices)
        result.append(symbol)
    
    random.shuffle(result)
    return "".join(result)
Então vamos tentar:
>>> 
>>> generate_random_string(8, string.digits)
... "59197550"
>>> 
>>> generate_random_string(8, string.ascii_letters, "!") 
... "vIOWXN!o"
Ótimo, é hora de realmente gerar uma senha que atenda a todos os nossos requisitos.
def generate_mydb_password(length: int) -> str:
    """
    Generate a random password for MyDB of a given `length`.
    The result has at least:
    - one uppercase letter
    - one lowercase letter
    - one digit
    - one special character
    Raises:
        ValueError -- If `length` is lesser than 8.
    """
    if length < 8:
        raise ValueError("Password length should be at least 8")
    return generate_random_string(
        length,
        string.ascii_uppercase, 
        string.ascii_lowercase, 
        string.digits, 
        "!&?", 
    )
Resta apenas verificar:
>>> generate_mydb_password(8)
... "P?P1&7zL"
>>> generate_mydb_password(13)
... "tR!QslK!Sl7EO"
>>> generate_mydb_password(2)
... ValueError: Password length should be at least 8
Total
Escrevemos um gerador de senhas fácil de entender e ao mesmo tempo aleatório, e ainda há muito tempo até o final do dia útil. Se não houver confiança na biblioteca random, você poderá substituí-la pela que desejar.
Obrigado pela atenção!