<gh_stars>0
import logging
import re
import typing
from collections import deque
from typing import NamedTuple, Any


class Node(NamedTuple):
    key: int
    name: str
    value: dict | list
    parent: Any

    def __repr__(self):
        if self.parent:
            return f"Node(key={self.key}, name='{self.name}', value={type(self.value)}, parent=Node({self.parent.key}, ...))"
        return f"Node(key={self.key}, name='{self.name}', value={type(self.value)}, parent=None)"

    def __hash__(self):
        return hash(self.key + hash(self.name) + hash(self.parent))

    def __eq__(self, other):
        return (self.key == other.key and self.name == other.name and
                self.value == other.value and self.parent == other.parent)

    def validate_as_primitive(self):
        if not isinstance(self.value, int | str | float):
            raise RuntimeError('List container was expected here')

    def validate_as_list(self):
        if not isinstance(self.value, list):
            raise RuntimeError('List container was expected here')

    def validate_as_dict(self):
        if not isinstance(self.value, dict):
            raise RuntimeError('Dictionary container was expected here')


class BaseYamlListener:
    """
    A class that represents the listener - a tree walker with events.

    The listener triggers event functions during walking the tree.
    An 'event function' is a method with 'enter_' or 'exit_' prefix in the name.

    As the walker encounter the node, it triggers 'enter_nodename(node)'.
    After the walker visits all children of the node, it triggers 'exit_nodename(node)'.

    The sample listener for specified tree structure can be generated by executing function
    print_implementation_code(starting_node).
    """

    def __init__(self, nodeproperty_dict: dict = None, stack: deque = None):
        """Constructs all the necessary attributes for the YAML Listener object

        :param nodeproperty_dict: (optional) A container which the main role is to annotate the various nodes.
            In the other words, this gives the way to associate arbitrary values with nodes (without manually
            altering the class code). The second role of this parameter is to provide external data to the Listener.

        :param stack: (optional) A container witch is used to return values to listener methods executing on
            nodes higher in the tree structure. The stack can store partial results: all what is required to do
            is push the result of computing a sub-node onto the stack. Then, in the higher node the 'exit' event
            function can pop the result.
        """
        self._key = 0
        self.nodeproperty_dict = nodeproperty_dict
        self.stack = stack

    def call_listening_member(self, prefix_str, node):
        """Generate the name of event function and try to run it

        :param prefix_str:
        :param node:
        :return:
        """
        listening_member_name = f"{prefix_str}{node.name}"  # create member name
        if hasattr(self, listening_member_name):  # check if this member is implemented (in inherited class)
            getattr(self, listening_member_name)(node)  # call this member
        else:
            logging.warning(
                f"'{listening_member_name}( Node({node.value.__class__.__name__} value) )' member function not implemented in {self.__class__.__name__} class")

    def go_deep(self, node):
        """Check if the node is 'conditional' and if it should be computed

        :param node: the examined node
        :return: True or False
        """
        t = type(node.value)
        if t is not dict:
            return True
        condition_flag = node.value.get('CONDITIONAL_NODE', None)
        if not condition_flag:
            return True
        defined_flags = self.nodeproperty_dict.get('CONDITIONAL_NODE', [])
        result = condition_flag in defined_flags
        if result:
            return True
        else:
            print(f'*** ommiting {node} ***')

    def walk(self, structure, internal_call=False, parent_node=None):
        """Walk the tree ('structure') and trigger events
        """
        if isinstance(structure, dict):
            # if BaseYamlListener.is_simple_dict(structure):
            #     return
            for name, value in structure.items():
                if BaseYamlListener.is_simple_type(value):
                    # pass this 'leaf' node over and continue walk
                    continue
                self._key += 1
                # actual_key = self.key
                current_node = Node(self._key, name, value, parent_node)
                if self.go_deep(current_node):
                    self.call_listening_member(f'enter_', current_node)
                    self.walk(value, True, current_node)
                    self.call_listening_member(f'exit_', current_node)
        elif isinstance(structure, list):
            # if BaseYamlListener.is_simple_list(structure):
            #     # omit every 'leaf'
            #     return
            for value in structure:
                self.walk(value, True, parent_node)
        if not internal_call:
            # clear node counter when walk is done
            self._key = 0

    @staticmethod
    def is_simple_dict(structure):
        # check if dictionary contains only simple types
        for value in structure.values():
            if not BaseYamlListener.is_simple_type(value):
                return False
        return True

    @staticmethod
    def is_simple_list(structure):
        # check if dictionary contains only simple types
        for value in structure:
            if not isinstance(value, (int, float, str)):
                return False
        return True

    @staticmethod
    def is_simple_type(structure):
        # check if dictionary contains only simple types
        return isinstance(structure, (int, float, str))

    def enter_info(self, node):
        self.print_node_info("encounter:  ", node)

    def exit_info(self, node):
        self.print_node_info("leave:      ", node)

    def print_node_info(self, description, node):
        print(description, node)
        if self.nodeproperty_dict and node in self.nodeproperty_dict:
            print(f"{' ' * len(description)} nodeproperty_dict[node] contains: {type(self.nodeproperty_dict[node])} ")

    def external(self, value):
        return re.sub(r'EXTERNAL_PROPERTY\((\w+)\)', self.get_external, value)

    def get_external(self, value):
        return self.nodeproperty_dict.get(value.groups()[0], value)

    @staticmethod
    def print_implementation_code(yaml_structure):
        """Print the sample code implementation for the provided tree structure
        """
        def walk2(structure, tmp_dict):
            if isinstance(structure, dict):
                # if BaseYamlListener.is_simple_dict(structure):
                #     # omit every 'leaf'
                #     return
                for key, value in structure.items():
                    if BaseYamlListener.is_simple_type(value):
                        # omit simple types
                        continue
                    tmp_dict[key] = True
                    walk2(value, tmp_dict)

            elif isinstance(structure, list):
                for value in structure:
                    walk2(value, tmp_dict)

        tmp_dict = dict()
        print("""
##################  BEGIN OF THE IMPLEMENTATION  ##################
from BaseYamlListener import BaseYamlListener

import yaml

class MyYamlListener(BaseYamlListener):""")
        walk2(yaml_structure, tmp_dict)
        for i, key in enumerate(tmp_dict, 1):
            if i < 3:
                print(f"""
    def enter_{key}(self, node):
        self.print_enter(node)  # 'the walker' encounters the node '{key}'
        
    def exit_{key}(self, node):
        self.print_exit(node)   # 'the walker' leaves the node '{key}' """)
            else:
                print(f"""
    def enter_{key}(self, node):
        self.print_enter(node)
        
    def exit_{key}(self, node):
        self.print_exit(node)""")
        print("""

def traverse(inputfile):
    walker = MyYamlListener()
    with open(inputfile, 'r', encoding='utf-8') as stream:
        yaml_structure = yaml.safe_load(stream)
        walker.walk(yaml_structure)
            
if __name__ == '__main__:
    traverse('input.yaml')
###################  END OF THE IMPLEMENTATION  ###################""")

import logging
import re
import typing
from collections import deque
from typing import NamedTuple, Any


class Node(NamedTuple):
    key: int
    name: str
    value: dict | list
    parent: Any

    def __repr__(self):
        if self.parent:
            return f"Node(key={self.key}, name='{self.name}', value={type(self.value)}, parent=Node({self.parent.key}, ...))"
        return f"Node(key={self.key}, name='{self.name}', value={type(self.value)}, parent=None)"

    def __hash__(self):
        return hash(self.key + hash(self.name) + hash(self.parent))

    def __eq__(self, other):
        return (self.key == other.key and self.name == other.name and
                self.value == other.value and self.parent == other.parent)

    def validate_as_primitive(self):
        if not isinstance(self.value, int | str | float):
            raise RuntimeError('List container was expected here')

    def validate_as_list(self):
        if not isinstance(self.value, list):
            raise RuntimeError('List container was expected here')

    def validate_as_dict(self):
        if not isinstance(self.value, dict):
            raise RuntimeError('Dictionary container was expected here')


class BaseYamlListener:
    """
    A class that represents the listener - a tree walker with events.

    The listener triggers event functions during walking the tree.
    An 'event function' is a method with 'enter_' or 'exit_' prefix in the name.

    As the walker encounter the node, it triggers 'enter_nodename(node)'.
    After the walker visits all children of the node, it triggers 'exit_nodename(node)'.

    The sample listener for specified tree structure can be generated by executing function
    print_implementation_code(starting_node).
    """

    def __init__(self, nodeproperty_dict: dict = None, stack: deque = None):
        """Constructs all the necessary attributes for the YAML Listener object

        :param nodeproperty_dict: (optional) A container which the main role is to annotate the various nodes.
            In the other words, this gives the way to associate arbitrary values with nodes (without manually
            altering the class code). The second role of this parameter is to provide external data to the Listener.

        :param stack: (optional) A container witch is used to return values to listener methods executing on
            nodes higher in the tree structure. The stack can store partial results: all what is required to do
            is push the result of computing a sub-node onto the stack. Then, in the higher node the 'exit' event
            function can pop the result.
        """
        self._key = 0
        self.nodeproperty_dict = nodeproperty_dict
        self.stack = stack

    def call_listening_member(self, prefix_str, node):
        """Generate the name of event function and try to run it

        :param prefix_str:
        :param node:
        :return:
        """
        listening_member_name = f"{prefix_str}{node.name}"  # create member name
        if hasattr(self, listening_member_name):  # check if this member is implemented (in inherited class)
            getattr(self, listening_member_name)(node)  # call this member
        else:
            logging.warning(
                f"'{listening_member_name}( Node({node.value.__class__.__name__} value) )' member function not implemented in {self.__class__.__name__} class")

    def go_deep(self, node):
        """Check if the node is 'conditional' and if it should be computed

        :param node: the examined node
        :return: True or False
        """
        t = type(node.value)
        if t is not dict:
            return True
        condition_flag = node.value.get('CONDITIONAL_NODE', None)
        if not condition_flag:
            return True
        defined_flags = self.nodeproperty_dict.get('CONDITIONAL_NODE', [])
        result = condition_flag in defined_flags
        if result:
            return True
        else:
            print(f'*** ommiting {node} ***')

    def walk(self, structure, internal_call=False, parent_node=None):
        """Walk the tree ('structure') and trigger events
        """
        if isinstance(structure, dict):
            # if BaseYamlListener.is_simple_dict(structure):
            #     return
            for name, value in structure.items():
                if BaseYamlListener.is_simple_type(value):
                    # pass this 'leaf' node over and continue walk
                    continue
                self._key += 1
                # actual_key = self.key
                current_node = Node(self._key, name, value, parent_node)
                if self.go_deep(current_node):
                    self.call_listening_member(f'enter_', current_node)
                    self.walk(value, True, current_node)
                    self.call_listening_member(f'exit_', current_node)
        elif isinstance(structure, list):
            # if BaseYamlListener.is_simple_list(structure):
            #     # omit every 'leaf'
            #     return
            for value in structure:
                self.walk(value, True, parent_node)
        if not internal_call:
            # clear node counter when walk is done
            self._key = 0

    @staticmethod
    def is_simple_dict(structure):
        # check if dictionary contains only simple types
        for value in structure.values():
            if not BaseYamlListener.is_simple_type(value):
                return False
        return True

    @staticmethod
    def is_simple_list(structure):
        # check if dictionary contains only simple types
        for value in structure:
            if not isinstance(value, (int, float, str)):
                return False
        return True

    @staticmethod
    def is_simple_type(structure):
        # check if dictionary contains only simple types
        return isinstance(structure, (int, float, str))

    def enter_info(self, node):
        self.print_node_info("encounter:  ", node)

    def exit_info(self, node):
        self.print_node_info("leave:      ", node)

    def print_node_info(self, description, node):
        print(description, node)
        if self.nodeproperty_dict and node in self.nodeproperty_dict:
            print(f"{' ' * len(description)} nodeproperty_dict[node] contains: {type(self.nodeproperty_dict[node])} ")

    def external(self, value):
        return re.sub(r'EXTERNAL_PROPERTY\((\w+)\)', self.get_external, value)

    def get_external(self, value):
        return self.nodeproperty_dict.get(value.groups()[0], value)

    @staticmethod
    def print_implementation_code(yaml_structure):
        """Print the sample code implementation for the provided tree structure
        """
        def walk2(structure, tmp_dict):
            if isinstance(structure, dict):
                # if BaseYamlListener.is_simple_dict(structure):
                #     # omit every 'leaf'
                #     return
                for key, value in structure.items():
                    if BaseYamlListener.is_simple_type(value):
                        # omit simple types
                        continue
                    tmp_dict[key] = True
                    walk2(value, tmp_dict)

            elif isinstance(structure, list):
                for value in structure:
                    walk2(value, tmp_dict)

        tmp_dict = dict()
        print("""
##################  BEGIN OF THE IMPLEMENTATION  ##################
from BaseYamlListener import BaseYamlListener

import yaml

class MyYamlListener(BaseYamlListener):""")
        walk2(yaml_structure, tmp_dict)
        for i, key in enumerate(tmp_dict, 1):
            if i < 3:
                print(f"""
    def enter_{key}(self, node):
        self.print_enter(node)  # 'the walker' encounters the node '{key}'
        
    def exit_{key}(self, node):
        self.print_exit(node)   # 'the walker' leaves the node '{key}' """)
            else:
                print(f"""
    def enter_{key}(self, node):
        self.print_enter(node)
        
    def exit_{key}(self, node):
        self.print_exit(node)""")
        print("""

def traverse(inputfile):
    walker = MyYamlListener()
    with open(inputfile, 'r', encoding='utf-8') as stream:
        yaml_structure = yaml.safe_load(stream)
        walker.walk(yaml_structure)
            
if __name__ == '__main__:
    traverse('input.yaml')
###################  END OF THE IMPLEMENTATION  ###################""")
A class that represents the listener - a tree walker with events.      The listener triggers event functions during walking the tree.     An 'event function' is a method with 'enter_' or 'exit_' prefix in the name.      As the walker encounter the node, it triggers 'enter_nodename(node)'.     After the walker visits all children of the node, it triggers 'exit_nodename(node)'.      The sample listener for specified tree structure can be generated by executing function     print_implementation_code(starting_node). Constructs all the necessary attributes for the YAML Listener object          :param nodeproperty_dict: (optional) A container which the main role is to annotate the various nodes.             In the other words, this gives the way to associate arbitrary values with nodes (without manually             altering the class code). The second role of this parameter is to provide external data to the Listener.          :param stack: (optional) A container witch is used to return values to listener methods executing on             nodes higher in the tree structure. The stack can store partial results: all what is required to do             is push the result of computing a sub-node onto the stack. Then, in the higher node the 'exit' event             function can pop the result. Generate the name of event function and try to run it          :param prefix_str:         :param node:         :return: # create member name # check if this member is implemented (in inherited class) # call this member Check if the node is 'conditional' and if it should be computed          :param node: the examined node         :return: True or False Walk the tree ('structure') and trigger events # if BaseYamlListener.is_simple_dict(structure): #     return # pass this 'leaf' node over and continue walk # actual_key = self.key # if BaseYamlListener.is_simple_list(structure): #     # omit every 'leaf' #     return # clear node counter when walk is done # check if dictionary contains only simple types # check if dictionary contains only simple types # check if dictionary contains only simple types Print the sample code implementation for the provided tree structure # if BaseYamlListener.is_simple_dict(structure): #     # omit every 'leaf' #     return # omit simple types ##################  BEGIN OF THE IMPLEMENTATION  ################## from BaseYamlListener import BaseYamlListener  import yaml  class MyYamlListener(BaseYamlListener): def enter_{key}(self, node):         self.print_enter(node)  # 'the walker' encounters the node '{key}'              def exit_{key}(self, node):         self.print_exit(node)   # 'the walker' leaves the node '{key}' def enter_{key}(self, node):         self.print_enter(node)              def exit_{key}(self, node):         self.print_exit(node) def traverse(inputfile):     walker = MyYamlListener()     with open(inputfile, 'r', encoding='utf-8') as stream:         yaml_structure = yaml.safe_load(stream)         walker.walk(yaml_structure)              if __name__ == '__main__:     traverse('input.yaml') ###################  END OF THE IMPLEMENTATION  ###################