Python Forum

Full Version: Tuple comprehension to for loop help
You're currently viewing a stripped down version of our content. View the full version with proper formatting.
Hello, I am currently taking a python course online. My class recently went over making a password checker api. If you take a look below, there is def get_password_leaks_count(hashes, hash_to_check).
How would I turn that tuple comprehension/generator expression into a for loop instead? I've been trying to figure out for the past 2 days and it just seems that I cannot get it right. I have rewatched the videos from the course, but I still cannot figure it out. Can someone show me what the answer would look like?

import requests
import hashlib
import sys

password_input = input("Please type in a password you would like to check ").split()

def request_api_data(query_char):
    url = "https://api.pwnedpasswords.com/range/" + query_char
    res = requests.get(url)
    if res.status_code != 200:
        raise RuntimeError(f'Error fetching: {res.status_code}, '
                           f'check the API and try again.')
    return res

def read_res(response):
    print(response.text)

def get_password_leaks_count(hashes, hash_to_check):
     hashes = (line.split(':') for line in hashes.text.splitlines())
     print(hashes)
     for h, count in hashes:
         if h == hash_to_check:
             return count
     return 0
     print(h, count)

def pwned_api_check(password):
    sha1password = hashlib.sha1(password.encode('utf-8')).hexdigest().upper()
    # Check password if it exists in API response
    first5_char, tail = sha1password[:5], sha1password[5:]
    response = request_api_data(first5_char)
    print(first5_char, tail)
    # print(response)
    # return read_res(response)
    return get_password_leaks_count(response, tail)


def main(args):
    for password in args:
        count = pwned_api_check(password)
        if count:
            print(f'{password} was found {count} times.'
                  f' Consider changing your password.')
        else:
            print(f'{password} was NOT found.')
    return "complete"
if __name__ == '__main__':
    sys.exit(main(password_file))
    # sys.exit(main(sys.argv[1:])
    # sys.exit(main(password_input))
Yes, I know there should not be same variable names, but this is just to show what I was trying to do.
My attempt:
def get_password_leaks_count(hashes, hash_to_check):
    for line in hashes.text.splitlines():
        hashes = line.split(':')
        # for h, count in hashes:
        #     if h == hash_to_check:
        #         return count
        # return 0
In the generator comprehension, hashes holds the iterator returned by the generator. In your attempt, hashes holds only one element from the iteration (one by one as you loop).

It's slightly easier to show the translation for a list comprehension since you're not having to yield.

For a list comprehension of the form
mylist = [x*2 for x in iter]
Then an unroll of it might look like:
temp_list = []
for x in iter:
    temp_list.append(x*2)
mylist = temp_list
Thank you for your reply.
I was finally able to get some meaningful help from the class.
def get_password_leaks_count(hashes, hash_to_check):
    splithashes = hashes.text.splitlines()
    newlist = []
    for line in splithashes:
        newlist.append(line.split(':'))
        for h, count in newlist:
            if h == hash_to_check:
                return count
        return 0
        print(h, count)
However, there is a weird problem now. Last night, this functioned well just like the original, but today, it is just letting any password pass by as being safe.
In your original (comprehension) code, hashes was built up from all the lines in passed in hashes. After it was built, you iterated through the contents.

In your new one you're doing both at the same time (incorrectly).

The loop starting at line 4 and the append on line 5 are building up the list. But then during the build-up, the loop on line 6 is trying to iterate through it. The first time it runs, there will be only one item, and it thinks that's the end of the list. In reality, the list wasn't complete.

These loops shouldn't be nested. They should either be separated as earlier (build up the list, then loop through it), or combined into one (check each hash rather than create the temporary list).