Q:

Why use time.sleep with random.expovariate in password check?

What exactly is the purpose of using the time.sleep() function in this snippet:
def check_user(user: User, password: str) -> bool:
    hashpass, salt = user_info[user].hashed_password
    target_hash_pass = hash_password(password, salt)[0]
    sleep(random.expovariate(10))
    return secrets.compare_digest(hashpass, target_hash_pass)

A:

It's an attempt to introduce a random time delay when verifying passwords, presumably to counter timing attacks, where an attacker makes use of the fact that incorrect passwords result in a faster response.
I'd not expect this to be that useful. secrets.compare_digest() already does everything right to mitigate timing attacks. Provided hashpass and target_hash_pass are the same type (both are bytes or both are strings, always), and have equal length, the usual timing attack vectors are not available here.
However, it could be that the author doesn't trust that those two conditions are always true. Perhaps the user_info structure contains shorter or longer password hashes or there is a chance that you'll get a different type. If so, then those issues should be addressed directly, instead.
It should be noted that because a timing attack compares the statistical differences between multiple attempts, using different passwords, random noise is only going to marginally slow such attacks; it only adds some more noise on top of whatever the timing differences network connections and normal computer operations already add. See Can I prevent timing attacks with random delays? and Could a random sleep prevent timing attacks?. Worse, the code uses the standard random random number generator, which isn't cryptographically secure and so the sleep variations could be accounted for given a determined enough attacker.
I'd strongly recommend to the author to remove that line, it doesn't offer any actual security here.

