Checking password strength while honoring functional requirements A program needs a password strength estimator but has several implementation restrictions:


Nothing outside of Python's standard library may be used.
A database of common passwords may not be consulted.
The code must be simple, self-contained, and easy to verify.


As for the interface of the password strength estimator, several requirements are provided:


Empty passwords must have a strength of zero.
Passwords may only use ASCII whitespace, alphanumeric, and punctuation characters.
Passwords with other characters must have a strength less than zero.
Otherwise, strength must be estimated as the bit-space needed to store the password.


In keeping with the caveats listed above, the following function appears to do as required:

import math
import string


def estimate_quality(password):
    if not password:
        return 0
    types = ([False, frozenset(string.whitespace)],
             [False, frozenset(string.ascii_lowercase)],
             [False, frozenset(string.ascii_uppercase)],
             [False, frozenset(string.digits)],
             [False, frozenset(string.punctuation)])
    for character in password:
        for index, (flag, group) in enumerate(types):
            if character in group:
                types[index][0] = True
                break
        else:
            return -1
    space = sum(len(group) for flag, group in types if flag)
    return math.ceil(math.log2(space) * len(password))


Is it possible to refactor the code to improve upon the third implementation restriction?
Your flags shouldn't be in the types definition.
types should be a constant, move it out of the function.
It'd be simpler if you used set.__isub__ and set.__and__, and looped through the types array.

It leaves all the unwanted values in the password too. Allowing for possible improvements on point 3.
You can join the sum and the base for loop together, removing the need for a types list.




TYPES = (
    frozenset(string.whitespace),
    frozenset(string.ascii_lowercase),
    frozenset(string.ascii_uppercase),
    frozenset(string.digits),
    frozenset(string.punctuation)
)

def estimate_quality(password):
    if not password:
        return 0

    password_set = set(password)
    space = 0
    for group in TYPES:
        if password_set &amp; group:
            space += len(group)
        password_set -= group

    if password_set:
        return -1

    return math.ceil(math.log2(space) * len(password))