#

Helper class that includes some frequently used routines

class Section(dict):

    def __init__(self, *args, **kwargs):
        self.update(*args, **kwargs)

        if not self.get('line'):
            self['line'] = 1
#

Check if there is some code

    def has_code(self):

        return bool(self["code_text"].strip())
#

Check if there are some docs

    def has_docs(self):

        return bool(self["docs_text"].strip())
#

Emulate collections.defaultdict behavior

    def __missing__(self, key):

        return ''
#
    def copy(self):
        return Section(**dict((k, v) for (k, v) in self.items()))
#

Helper class that handles a list of methods and allows to insert item before or after some specific item.

class ParsingStrategy(list):
#

Initialize from an arbitrary sequence of arguments

    def __init__(self, *args):

        for arg in args:
            self.append(arg)
#

Return the index of a method which name is name or raise an exception.

    def index(self, name):

        index = next((i for i, x in enumerate(self) if x.__name__ == name), None)
        if not index:
            raise Exception("Step '{0}' not found in the strategy: {1}".format(name, self))

        return index
#

Insert a method before an item with a name key

    def insert_before(self, key, method):

        self.insert(self.index(key), method)
#

Insert a method after an item with a name key

    def insert_after(self, key, method):

        self.insert(self.index(key) + 1, method)
#

Remove the item with a name key

    def delete(self, key):

        self.pop(self.index(key))
#

Helper decorator to iterate through the sections while altering them.

param start Section index to start with. param increment Index increment. Use -1 to iterate backwards.

def iterate_sections(start=1, increment=1):

    def wrap(f):
#
        def wrapped_f(self, sections):
            i = start
            while i < len(sections) and i > -1:
                new_i = f(self, sections, i)
                i = new_i if new_i else i + increment

            return sections

        wrapped_f.__name__ = f.__name__
        return wrapped_f
#
    return wrap
#

Helper method that splits a section into parts using the regex matching against the section code

def split_section_by_regex(section, regex, meta=None):

    if not section.has_code():
        return [section]

    sections = []
    start = 0
    for match in regex.finditer(section["code_text"]):
        code = section["code_text"][start:match.start()]
        if code.strip():
            sections.append(Section(code_text=code))
        sections.append(Section(docs_text=match.group(1), meta=meta))
        start = match.end()

    remains = section["code_text"][start:].strip('\n')
    if remains.strip():
        sections.append(Section(code_text=remains))

    return sections
#
def split_code_by_pos(i, pos, sections):
    code = sections[i]["code_text"]
    section_1 = sections[i].copy()
    section_1['code_text'] = code[:pos]
    section_2 = sections[i].copy()
    section_2['docs_text'] = ""
    section_2['code_text'] = code[pos:]

    sections[i:i+1] = [section_1, section_2]
    return sections