Search code examples
pythonpython-3.xmodulegeneratorpython-itertools

How to filter some combinations from being generated in product function of itertools of python?


I'm working on a some kind of password generated some sort of program, using the itertools module of the python, with its product function i'm trying to generate unique passwords that are NOT already saved in the database i'm having.

The problem is: When i'm trying to generate more passwords, i need to wait a hell of a time! for the new password to come out, i've already written the code to filter the passwords that are already within the database BUT the product function STILL processes the passwords EVEN if they are already used, then it prints out the passwords that aren't used already which still takes the same amount of time which is just too much! Now i know that i could use my GPU for this process, but i'm sure there's gotta be a logical operation to stop this function from generating the already used passwords.

Here's the source code (don't pay too much attention to the other parts of the code, i know it doesn't work by itself because i'm using this Generator function in another kind of master main program):

from string import *
from itertools import product

# The password generator function.
# a little detail about the parameters:
# From = password starts from how many characters
# To = password ends with how many characters
# Fname = stands for 'file name' which is actually the database name
# cn, Base = these are just to organize different databases, not too much important in this case
def Generator(From, To, Fname, cn, Base):
    # Defining the database
    global db
    db = Fname+cn+'.txt'
    # Listing all the already used passwords in the 'lines' list
    global lines
    lines = []
    mainDB = open(Fname+str(Base)+'.txt')
    lines = mainDB.readlines()
    mainDB.close()
    # Defining the characters which (probably) can't be used as passwords(due to injection attacks and so on...)
    Blocked = ",:?/\\|<>*"+"'"+'"'
    # The ones that i'm not sure if they have to be blocked... but i'm blocking them anyway :|
    proBlocked = ";[]{}()+=^`"
    Blocked += proBlocked
    chars = ascii_letters + digits + punctuation
    # Deleting all the blocked characters from the main 'chars' string
    for c in Blocked:
        chars = chars.replace(c,'')

    # Going for generating the password trying every single password we can make and take a unique one out for use
    for i in range(From,To):
        # Starting the `product` module which is the main element of the program
        for j in product(chars, repeat=i):
            # Defining the 'word' container(or the 'password' container if you will)
            global word
            # Putting the generated word in the container(variable)
            word = "".join(j)
            # Opening the database
            p = open(db, '+r')
            # Checking if the password has already been used and added to the main database
            if not(word in str(lines)):
                # Adding to the database and returning the unique password
                p.write(p.read() + word + '\n')
                p.close()
                return word

Here's the main program:

from Generator import Generator
import os
import sys

# This part needs some more work, like designing and so on
# right now we're just testing.
while True:
    os.system('cls')
    inp = input("Type 1 to start generating, or type 0 to close the program:\n")
    if inp.isdigit():
        if inp == '1':
            break
        elif inp == '0':
            sys.exit()

print("Here's your unique password: "+Generator(4, 10, "MainDB", 1, 1))

This is how the program's output looks like:

Type 1 to start generating, or type 0 to close the program:
1
Here's your unique password: aaaa

Now by filtering more passwords within the database, the passwords wouldn't be things like 'aaaa' and they'll be nicer and sometimes mean something and can be remembered (instead of using random though cause i already have a program for that one)

Really appreciate your help <3 again, the problem is: i'm looking for a way to filter the already used password IN the product module ITSELF to stop those from being EVEN generated to save a lot of time.

Thanks in advance!


Solution

  • "Filtering out the already used passwords" really comes down to checking the output of the product function against some set of used passwords. (Even if you wrote your own special "product_with_exclusions" function, it would need to work that way.)

    Your code already does this checking, but it can be made faster. On every iteration of the loop over the results of product, your code in the question turns the list of passwords into a string and checks the output against that. Repeatedly converting list -> str and checking whether the password is part of that big string is slow. It's better to (1) not do the conversion to str, (2) instead of a list, use a set (it's much quicker to check whether a value is in a large set, compared to the same-size list).

    With that, the optimised code could look something like this. (This includes a few other tidyups - removed unnecessary globals, used with-blocks when opening files, and wrote the new password in append mode.)

    from string import *
    from itertools import product
    
    # The password generator function.
    # a little detail about the parameters:
    # From = password starts from how many characters
    # To = password ends with how many characters
    # Fname = stands for 'file name' which is actually the database name
    # cn, Base = these are just to organize different databases, not too much important in this case
    def Generator(From, To, Fname, cn, Base):
        # Defining the database
        db = Fname+cn+'.txt'
        
        # Listing all the already used passwords in the 'lines_set' set for fast lookup
        with open(Fname+str(Base)+'.txt') as mainDB:
            lines_set = {l.strip() for l in mainDB.readlines()}
    
        # Defining the characters which (probably) can't be used as passwords(due to injection attacks and so on...)
        Blocked = ",:?/\\|<>*"+"'"+'"'
        
        # The ones that i'm not sure if they have to be blocked... but i'm blocking them anyway :|
        proBlocked = ";[]{}()+=^`"
        Blocked += proBlocked
        chars = ascii_letters + digits + punctuation
        
        # Deleting all the blocked characters from the main 'chars' string
        chars = [c for c in chars if c not in Blocked]
    
        # Going for generating the password trying every single password we can make and take a unique one out for use
        for i in range(From,To):
            # Starting the `product` module which is the main element of the program
            for j in product(chars, repeat=i):
                # Putting the generated word in the container(variable)
                word = "".join(j)
    
                # Checking if the password has already been used and added to the main database
                if word not in lines_set:
                    # Adding to the database and returning the unique password
                    with open(db, 'a') as p: # Open in append mode so we just write one new line
                        p.write(word + '\n')
                    return word