コード例 #1
0
    def update_waiting(self):
        """
        Update function, runs if state is WAITING.

        Function takes value from description (desc_value). If this value
        is a keyword ($vars and utils.program_values) then it generates
        corresponding output. Otherwise function takes value from input.

        After preparations function prints value and sets state to ACTIVE.
        """
        value = self.desc_value
        if value == "$vars":
            variables = []
            for var in self.variables.items():
                if var[0].startswith(f"{self.scope}$"):
                    variables.append((var[0][len(f"{self.scope}$"):], var[1]))
            value = dict(variables)
        elif utils.program_values(value, True):
            value = utils.program_values(value)
        else:
            value = self.get_value(0)
            value = value if value is not None else self.desc_value
        logger.log_message(value)
        if base.Node.ut_catch is not None:
            check_value = base.Node.ut_catch.pop(0)
            if check_value is not None and check_value != value:
                logger.log_error(f"expected value '{check_value}'")
        self.state = INACTIVE
コード例 #2
0
ファイル: control.py プロジェクト: octo-gone/sync-execution
    def update_waiting(self):
        """
        Update function, runs if state is WAITING.

        Log error if function runs (logger will raise exception).
        """
        logger.log_error(f"node 'error' raised exception '{self.desc_value}'")
コード例 #3
0
    def update_waiting(self):
        """
        Update function, runs if state is WAITING.

        Prompt is value from node description (desc_value).
        Function tries to find construction of type {<var name>$<type>} in prompt.

        If it was found then function tries to generate string
        version of value from variable.

        If description wasn't changed then function takes '>>> ' (default) as prompt otherwise desc_value.

        After taking value from input, function saves it to output and changes state to ACTIVE.
        """
        pattern = r"{(?P<var>[^\{\}]+?)(?:\$(?P<type>[^\{\}]+?))?}"
        self.desc_value = saxutils.unescape(self.desc_value)
        # TODO: optimize
        match = list(re.finditer(pattern, self.desc_value))
        while match:
            match = match[0]
            var_name = f"{self.scope}$" + match["var"]
            var_type = match["type"]
            if var_type is not None:
                if var_type in utils.types_default:
                    var_type = utils.types_default[var_type].__class__
                else:
                    var_type = utils.coercion
            if var_name in self.variables:
                var_value = self.variables[var_name]
                if var_type is not None:
                    var_value = var_type(var_value)
                self.desc_value = list(self.desc_value)
                self.desc_value[match.span()[0]:match.span()[1]] = list(
                    str(var_value))
                self.desc_value = "".join(self.desc_value)
            match = list(re.finditer(pattern, self.desc_value))
        prompt = ">>> "
        if self.desc_value:
            if self.desc_value.endswith(" "):
                prompt = self.desc_value
            else:
                prompt = self.desc_value + " "
        if base.Node.ut_send is not None:
            if not base.Node.ut_send:
                logger.log_error("unit tests are empty")
            value = base.Node.ut_send.pop(0)
            value = logger.Color.colored_input(prompt, value=value)
        else:
            value = logger.Color.colored_input(prompt)
        self.set_value(utils.coercion(value), 0)
        self.state = ACTIVE
コード例 #4
0
    def update_waiting(self):
        """
        Update function, runs if state is WAITING.

        If node has both input values then function checks zero division.

        If divider not equal to zero then function evaluates output value
        and changes state otherwise function raises error.
        """
        if self.get_value(0) is not None and self.get_value(1) is not None:
            if self.get_value(1) == 0:
                logger.log_error(f"node 'div' raised exception 'Zero Division'")
            self.set_value(self.get_value(0) // self.get_value(1), 0)
            self.state = ACTIVE
コード例 #5
0
ファイル: struct.py プロジェクト: octo-gone/sync-execution
    def update_waiting(self):
        """
        Update function, runs if state is WAITING.

        Function takes name of value from node description (desc_value)
        and generates name of variable based on node scope.

        If key or value types are not acceptable then
        function raises errors. Possible types are in utils.types.

        Variable of type 'dict' has next description:
            structure - name of structure (dict);
            key_type - type of all keys in structure;
            value_type - type of all value in structure;
            values - dict of values in structure.
        """
        key_type = self.get_value(0)
        if key_type not in utils.types and key_type is not None:
            key_type = type(key_type)
            if key_type not in utils.types:
                logger.log_error(
                    f"unaccepted type for key in node '{self.name}/{self.id}' "
                    f"with name '{self.desc_value}'")
        value_type = self.get_value(1)
        if value_type not in utils.types and value_type is not None:
            value_type = type(value_type)
            if value_type not in utils.types:
                logger.log_error(
                    f"unaccepted type for value in node '{self.name}/{self.id}' "
                    f"with name '{self.desc_value}'")
        desc_value = f"{self.scope}$" + self.desc_value
        if key_type is not None and value_type is not None:
            self.struct_variables[desc_value] = {
                "structure": "dict",
                "key_type": key_type,
                "value_type": value_type,
                "values": {}
            }
            self.state = ACTIVE
コード例 #6
0
def run(n, w, s, limit=10**5):
    """
    Function iteratively launches nodes update functions for
    different states: INACTIVE, WAITING and ACTIVE. If node stop
    or similar generates exception StopSync, then function stops
    iteration, otherwise if the iteration exceeds the limit
    function stops updating.

    :param n: list of nodes information
    :param w: list of wires information
    :param s: list of scope information
    :param limit: limit number of updates
    """
    create_structure(n, w, s)
    if "run" not in map(lambda x: x.name, base.Node.nodes.values()) and \
            "unit test" not in map(lambda x: x.name, base.Node.nodes.values()):
        logger.log_error("no 'start' node")
    logger.log_success("program started")
    for utils.iteration in range(limit):
        try:
            nodes = list(base.Node.nodes.values())
            random.shuffle(nodes)
            for node in nodes:
                node.update(base.INACTIVE)
            for node in nodes:
                node.update(base.WAITING)
            for node in nodes:
                node.update(base.ACTIVE)
        except exceptions.StopSync:
            logger.log_success("program stopped via node 'stop'")
            utils.iteration = -1
            break
    if utils.iteration != -1:
        active_nodes = []
        for node in base.Node.nodes.values():
            if node.state == base.WAITING:
                active_nodes.append((node.name, node.id))
        if active_nodes:
            active_nodes = ',\n\t'.join(
                map(lambda x: f"{x[0]} (id: {x[1]})", active_nodes))
            active_nodes = f"\nActive nodes:[\n\t{active_nodes}\n]"
        else:
            active_nodes = ""
        logger.log_error(f"iteration overstepped the limit{active_nodes}")
    if base.Node.ut_send:
        logger.log_error("value input expected")
    elif base.Node.ut_catch:
        logger.log_error(f"expected value '{base.Node.ut_catch}'")
コード例 #7
0
ファイル: base.py プロジェクト: octo-gone/sync-execution
    def __init__(self, data):
        """
        Wire constructor. Attribute takes dictionary with
        several descriptive parameters:
            source - identifier of the first connected object in scheme;
            target - identifier of the second connected object in scheme;
            exitX/exitY - connection (exit) point in source object;
            entryX/entryY - connection (entry) point in target object;
        It checks validity of connected objects and
        calculates entry and exit connector indexes

        :param dict data: information about wire
        """
        self.wires.append(self)
        self.raw_data = data

        self.source = Node.nodes.get(data["source"], None)
        self.target = Node.nodes.get(data["target"], None)

        if self.source is None and self.target is None:
            logger.log_warning("no source and target nodes found")
        elif self.source is None:
            logger.log_error(
                f"no source node found for wire with target '{self.target.name}/{self.target.id}'"
            )
        elif self.target is None:
            logger.log_error(
                f"no target node found for wire with source '{self.source.name}/{self.source.id}'"
            )

        self.exit = data["exitX"], data["exitY"]
        self.entry = data["entryX"], data["entryY"]

        if self.exit[0] == self.entry[0]:
            logger.log_error(
                f"wrong wire connection from node '{self.source.name}/{self.source.id}' "
                f"to node '{self.target.name}/{self.target.id}'")

        if self.exit[0] == 0:
            self.exit, self.entry = self.entry, self.exit
            self.target, self.source = self.source, self.target

        self.entry_connector = self.target.input_connectors.index(
            self.entry[1])
        self.exit_connector = self.source.output_connectors.index(self.exit[1])
コード例 #8
0
ファイル: base.py プロジェクト: octo-gone/sync-execution
    def update_connections(self):
        """
        Function generates information about the connections
        of node inputs and inputs on the diagram. Checks whether
        the connection is correct. Creates relations between
        nodes, this requires working with the class Wire.
        """
        for i in range(len(self.inputs)):
            wires = Wire.get_nodes_from_input(self, i)
            self.inputs[i] = wires

        offset = 0
        ins = []
        if str(self.name).startswith("function"):
            inputs_info = self.raw_data["inputs_names"]
            outputs_info = self.raw_data["outputs_names"]
        else:
            inputs_info = nodes_info.nodes_info[self.name]["inputs"]
            outputs_info = nodes_info.nodes_info[self.name]["outputs"]

        self.actual_inputs = {}
        for i, v in enumerate(inputs_info):
            variant = v[0]
            other = v[1:]
            if variant == SINGLE or SMALL in other:
                ins.append(sum(self.inputs[offset:offset + 1], []))
                self.actual_inputs[i] = tuple(range(offset, offset + 1))
                offset += 1
            elif variant == MULTIPLE:
                if DIRECTED not in other:
                    ins.append(sum(self.inputs[offset:offset + 7], []))
                    self.actual_inputs[i] = tuple(range(offset, offset + 7))
                    offset += 7
                else:
                    ins.append(sum(self.inputs[offset:offset + 5], []))
                    self.actual_inputs[i] = tuple(range(offset, offset + 5))
                    offset += 5

        self.actual_outputs = {}
        offset = 0
        for i, v in enumerate(outputs_info):
            variant = v[0]
            other = v[1:]
            if variant == SINGLE or SMALL in other:
                self.actual_outputs[i] = tuple(range(offset, offset + 1))
                offset += 1
            elif variant == MULTIPLE:
                if DIRECTED not in other:
                    self.actual_outputs[i] = tuple(range(offset, offset + 7))
                    offset += 7
                else:
                    self.actual_outputs[i] = tuple(range(offset, offset + 5))
                    offset += 5

        for i, v in enumerate(ins):
            variant = inputs_info[i][0]
            if variant == SINGLE and len(v) > 1:
                logger.log_error(
                    f"wrong input connections count in node '{self.name}/{self.id}'"
                )

        self.inputs = ins

        for i in range(len(self.outputs)):
            self.outputs[i] = Wire.get_nodes_from_output(self, i)

        offset = 0
        outs = []
        self.output_values = []
        for i in outputs_info:
            variant = i[0]
            other = i[1:]
            if variant == SINGLE or SMALL in other:
                outs.append(sum(self.outputs[offset:offset + 1], []))
                offset += 1
            elif variant == MULTIPLE:
                if DIRECTED not in other:
                    outs.append(sum(self.outputs[offset:offset + 7], []))
                    offset += 7
                else:
                    outs.append(sum(self.outputs[offset:offset + 5], []))
                    offset += 5

        self.outputs = outs
        self.output_values = []
        for _ in self.outputs:
            self.output_values.append(None)
コード例 #9
0
ファイル: parser.py プロジェクト: octo-gone/sync-execution
def parse(file_path):
    """
    Function parse uncompressed .drawio file and returns all nodes and all connected wires with description
    :param file_path: path to .drawio file
    :return: tuple (nodes, wires, scopes)
    """

    for alias, node_class in loader.node_aliases.items():
        if alias not in nodes_info.nodes_info:
            nodes_info.nodes_info.update({alias: node_class.desc})

    if os.path.exists(file_path):
        with open(file_path, "r", encoding="utf-8") as file:
            data = unescape("".join(file.read().split("\n")))
            data = "'".join(data.split("&#39;"))
    elif not file_path:
        logger.log_error(f"no file to run")
    else:
        logger.log_error(f"file with name \"{file_path}\" not found")

    res = ""
    remove_data = []
    for diag in re.finditer(diagram_pattern, data):
        remove_data.append(
            (diag['diagram'], coder.from_library(diag['diagram'])))
        res += coder.from_library(diag['diagram'])

    return_data = copy(data)
    for d in remove_data:
        return_data = return_data.replace(d[0], d[1])

    data = res
    if not data:
        logger.log_error("no data in diagram")

    nodes = []
    wires = []

    for node in re.finditer(node_pattern, data):
        patterns = {
            "id": id_pattern,
            "img": image_pattern,
            "value": value_pattern,
            "x": x_pattern,
            "y": y_pattern,
            "width": width_pattern,
            "height": height_pattern,
            "node_name": node_name_pattern,
            "fixed_scope": fixed_scope_pattern
        }
        node = node.group()
        if not str(re.search(patterns["node_name"],
                             node).group("node_name")).startswith("function"):
            for key in patterns.keys():
                res = re.search(patterns[key], node)
                if key == "value":
                    patterns[key] = res.group("value0") if res.group(
                        "value0") is not None else res.group("value1")
                else:
                    if res:
                        patterns[key] = res.group(key)
                    else:
                        patterns[key] = None

            if patterns["node_name"] not in nodes_info.nodes_info:
                logger.log_error(
                    f"no built-in nodes found with name '{patterns['node_name']}'"
                )

            inputs = get_connectors(
                nodes_info.nodes_info[patterns["node_name"]]["inputs"])
            outputs = get_connectors(
                nodes_info.nodes_info[patterns["node_name"]]["outputs"])

            ratio = (1,
                     max(
                         get_ratio(nodes_info.nodes_info[patterns["node_name"]]
                                   ["inputs"]),
                         get_ratio(nodes_info.nodes_info[patterns["node_name"]]
                                   ["outputs"])))

            inputs = list(map(lambda x: round(x / ratio[1], 5), inputs))
            outputs = list(map(lambda x: round(x / ratio[1], 5), outputs))

            patterns["inputs"] = inputs
            patterns["outputs"] = outputs

            nodes.append(patterns)
        else:
            patterns["points"] = points_pattern
            patterns["inputs"] = inputs_pattern
            patterns["outputs"] = outputs_pattern
            for key in patterns.keys():
                res = re.search(patterns[key], node)
                if res:
                    if key == "value":
                        patterns[key] = res.group("value0") if res.group(
                            "value0") else res.group("value1")
                    else:
                        patterns[key] = res.group(key)
                        if key in ("points", "inputs", "outputs"):
                            patterns[key] = eval(res.group(key))
                else:
                    patterns[key] = None
                    if key in ("points", "inputs", "outputs"):
                        patterns[key] = []

            inputs = get_connectors(patterns["inputs"])
            outputs = get_connectors(patterns["outputs"])

            ratio = (1,
                     max(get_ratio(patterns["inputs"]),
                         get_ratio(patterns["outputs"])))

            if patterns["node_name"].startswith("function input") or \
               patterns["node_name"].startswith("function output"):
                ratio = (ratio[0], max(2, ratio[1]))

            inputs = list(map(lambda x: round(x / ratio[1], 5), inputs))
            outputs = list(map(lambda x: round(x / ratio[1], 5), outputs))

            patterns["inputs_names"] = patterns["inputs"]
            patterns["inputs"] = inputs
            patterns["outputs_names"] = patterns["outputs"]
            patterns["outputs"] = outputs

            nodes.append(patterns)

    for wire in re.findall(wire_pattern, data):
        patterns = {
            "id": id_pattern,
            "exitX": exitX_pattern,
            "exitY": exitY_pattern,
            "entryX": entryX_pattern,
            "entryY": entryY_pattern,
            "source": source_pattern,
            "target": target_pattern,
        }

        for key in patterns.keys():
            res = re.search(patterns[key], wire)
            if res:
                patterns[key] = res.group(key)
            else:
                patterns[key] = None

        nodes_ids = dict(map(lambda x: (x["id"], x["node_name"]), nodes))
        connected = True
        for key in ["exitY", "entryY"]:
            if patterns[key] is None:
                if patterns['source'] in nodes_ids:
                    logger.log_error(
                        f"wrong connected wire found from node "
                        f"'{nodes_ids[patterns['source']]}/{patterns['source']}'"
                    )
                elif patterns['target'] in nodes_ids:
                    logger.log_error(
                        f"wrong connected wire found to node "
                        f"'{nodes_ids[patterns['target']]}/{patterns['target']}'"
                    )
                else:
                    logger.log_error(f"unconnected wire found")
                connected = False
            if connected:
                patterns[key] = round(float(patterns[key]), 5)

        for key in ["exitX", "entryX"]:
            if patterns[key] is not None:
                patterns[key] = round(float(patterns[key]), 5)

        if connected:
            wires.append(patterns)

    scopes = []
    for scope in re.findall(scope_pattern, data):
        patterns = {
            "id": id_pattern,
            "value": value_pattern,
            "x": x_pattern,
            "y": y_pattern,
            "width": width_pattern,
            "height": height_pattern,
        }
        for key in patterns.keys():
            res = re.search(patterns[key], scope)
            if res:
                patterns[key] = res.group(key)
            else:
                patterns[key] = None

        scopes.append(patterns)

    with open(file_path, "w", encoding="utf-8") as file:
        file.write(return_data)

    return nodes, wires, scopes