Purpose: To allow nested For Loops with a minimum of syntax change from the standard For Loop.Reason: I want to nest For Loops in Robot Framework. The code is working perfectly as a custom library/keyword in Robot Framework, but I'm asking for input to improve the code. I have no formal training in Python. This script was how I learned the language, so please don't expect it to be Pythonic, I think the term is.Sample Robot Framework code to run this keyword:*** Settings ***Library     ExtendedSelenium2LibraryLibrary     CollectionsLibrary     Loops.py*** Variables ***${gold_squadron} =  Gold${red_squadron} =   Red*** Test Cases ***Test For Loop    For Loop    IN RANGE    0   1   INDEX0    ...     \\  For Loop    IN RANGE    1   6   INDEX1    ...     \\  \\  Assign Internal Variable    {{standing_by}}     Standing By Red Leader    ...     \\  \\  Run Keyword If      INDEX1 == 1     Log to Console  ${red_squadron} Leader Standing By    ...     \\  \\  Run Keyword Unless  INDEX1 == 1     Log to Console  ${red_squadron} INDEX1 {{standing_by}}    ...     \\  For Loop    IN RANGE    1   6   INDEX2    ...     \\  \\  Assign Internal Variable    {{standing_by_2}}   Standing By Gold Leader    ...     \\  \\  Run Keyword If      INDEX2 == 1     Log to Console  ${gold_squadron} Leader Standing By    ...     \\  \\  Run Keyword Unless  INDEX2 == 1     Log to Console  ${gold_squadron} INDEX2 {{standing_by_2}}And here is the code I'm working on.from robot.libraries.BuiltIn import BuiltIn# TODO: Create new types of For Loops and While Loopsclass Loops(object):    def __init__(self):        self.selenium_lib = BuiltIn().get_library_instance('ExtendedSelenium2Library')        self.internal_variables = {}    def for_loop(self, loop_type, start, end, index_var, *keywords):        #   Format the keywords        keywords = self._format_loop(*keywords)        #   Clean out the internal variables from previous iterations        self.internal_variables = {}        #   This is the actual looping part        for loop_iteration in range(int(start), int(end)):            keyword_set = self._index_var_swap(loop_iteration, index_var, *keywords)            #   If it's a one-keyword list with no arguments, then I can use the fastest possible keyword to run it            if len(keyword_set) == 1:                BuiltIn().run_keyword(keyword_set)            #   If it's a one-keyword list with arguments, then I can use a faster keyword to run it            elif 'AND' not in keyword_set:                BuiltIn().run_keyword(*keyword_set)            #   If it's a multiple-keyword list, then I have to use Run Keywords            else:                BuiltIn().run_keywords(*keyword_set)    def _format_loop(self, *keywords):        keywords = list(keywords)   # I need to format the keywords as a list.        changed = False             # Whether or not I changed anything in the previous iteration.        index = 0                   # The item index I'm at in the list of keywords        del_list = []               # The list of items I need to delete        swap_list = []              # The list of items i need to swap to AND for the use of Run Keywords        #   For each argument        for x in keywords:            #   Format it to a string            x = str(x)            #   If the keyword in question happens to be one of the 'Assign Internal Variable' keywords, then I need            #       to run it now, not later.            #   By splitting it up, I add a little complexity to the code but speed up execution when you're just            #       assigning a scalar variable as opposed to having to search through the next few items just to find            #       what I know is just going to be the next one.            #   So, if it's the simple assignment...            if x.lower() == 'assign internal variable':                #   ...run the Assign Internal Variable keyword with the two inputs                BuiltIn().run_keyword(x, *keywords[int(index)+1:int(index)+3])            #   If it's the more complicated variable...            elif x.lower() == 'assign internal variable to keyword':                #   ...initialize variables...                deliminator_search = 0                k_check = x                #   ...search the next few keywords for a deliminator...                while k_check != '\\' and k_check != '\\\\':                    deliminator_search = deliminator_search + 1                    k_check = keywords[int(index)+deliminator_search]                #   ...and run the Assign Internal Variable to Keyword keyword with the found keyword                BuiltIn().run_keyword(x, *keywords[int(index)+1:int(index)+2+deliminator_search])            #   If the previous element was not changed...            if not changed:                #   If the current item is not the last one on the list...                if x != len(keywords) - 1:                    #   If the current item is a deliminator...                    if x == '\\':                        #   If the next item is a deliminator, delete this item and set changed to True                        if keywords[int(index) + 1] == '\\':                            del_list.append(index)                            changed = True                        #   If the next item is not a deliminator...                        else:                            #   If this isn't the first deliminator on the list, swap it to an 'AND'                            if index != 0:                                swap_list.append(index)                                changed = True                            #   If this deliminator is in position index=0, just delete it                            else:                                del_list.append(index)                                changed = True                    #   If the current element is not a deliminator, then I don't need to touch anything.                #   If the current element is the last one, then I don't need to touch anything            #   If the previous element was changed, then I don't need to change this one...            elif changed:                changed = False                #   ...but if it's a deliminator then I do need to set it up for the inner for loop it means.                if keywords[index] == '\\':                    keywords[index] = '\\\\'            index = index + 1   # Advance the index        # These actually do the swapping and deleting        for thing in swap_list:            keywords[thing] = 'AND'        del_list.reverse()        for item in del_list:            del keywords[item]        # I also need to activate my variables for this set of keywords to run.        keywords = self._activate_variables(*keywords)        return keywords    @staticmethod    def _index_var_swap(loop_iteration, index_var, *keywords):        #   Format the keywords as a list for iteration        keywords = list(keywords)        index = 0        #   For every line in keywords        for line in keywords:            #   Replace all instances of the index_var in the string with the loop iteration as a string            keywords[index] = str(line).replace(str(index_var), str(loop_iteration))            index = index + 1        return keywords    def assign_internal_variable(self, variable_name, assignment):        # This keyword works like any other keyword so that it can be activated by BuiltIn.run_keywords        # The syntax for an internal variable is '{{varName}}' where varName can be anything        self.internal_variables[variable_name] = assignment    def assign_internal_variable_to_keyword(self, variable_name, keyword, *assignment):        # This keyword works like any other keyword so that it can be activated by BuiltIn.run_keywords        # The syntax for an internal variable is '{{varName}}' where varName can be anything        self.internal_variables[variable_name] = BuiltIn.run_keyword(keyword, *assignment)    def _activate_variables(self, *keywords):        #   Initialize variables        keywords = list(keywords)   # Cast keywords as a List        index = 0                   # The index of the keyword I'm looking at        #   For each keyword        for keyword in keywords:            keyword = str(keyword)  # Cast keyword as a String            assignment = False      # Whether or not the found variable name is in a variable assignment            for key in self.internal_variables.keys():                key = str(key)      # Cast key as a String                #   If I can find the key in the keyword and it's not an assignment...                if keyword.find(key) > -1 and not assignment:                    #   ...replace the text of the key in the keyword.                    keywords[index] = keyword.replace(str(key), str(self.internal_variables[key]))                #   If the keyword I'm looking at is an assignment...                if keyword.lower() == 'assign internal variable'\                        and keyword.lower() != 'assign internal variable to keyword':                    #   ...then my next keyword is going to definitely be a known variable, so I don't want to touch it.                    assignment = True                #   If the keyword I'm looking at is not an assignment...                else:                    #   ...set assignment to False just in case the previous one happened to be an assignment.                    assignment = False            index = index + 1   # Advance the index        return keywords     # Return the list of keywords to be used in the format loopI'm mostly looking for feedback and optimization suggestions.
Python - Robot Framework Loops
python;robotframework