Esempio n. 1
0
    def to_dict(self) -> Dict:
        """Return a serialized representation of the Wait state."""
        data = {"Type": "Wait"}

        for keyword in self.ast_node.value.keywords:
            if keyword.arg == "seconds":
                if isinstance(keyword.value, ast.Num):
                    data["Seconds"] = keyword.value.n
                else:
                    data["SecondsPath"] = convert_input_data_ref(keyword.value)
            elif keyword.arg == "timestamp":
                if isinstance(keyword.value, ast.Str):
                    data["Timestamp"] = keyword.value.s
                else:
                    data["TimestampPath"] = convert_input_data_ref(
                        keyword.value)
            else:
                raise UnsupportedOperation(
                    f"Valid keyword arguments include `seconds` and `timestamp` but `{keyword.arg}` was provided",
                    self.ast_node,
                )

        self._set_end_or_next(data)

        return data
Esempio n. 2
0
    def _get_result_path(self) -> Optional[str]:
        """Get the ResultPath value for the map state

        If the result path was not explicitly provided, return None to indicate that
        the the result should be discarded.

        Returns:
            result path string or None

        """
        if isinstance(self.ast_node, ast.Assign) and len(self.ast_node.targets) > 0:
            return convert_input_data_ref(self.ast_node.targets[0])

        return None
    def visit_Dict(self, node: Any) -> None:
        """Visit a dict"""
        key_nodes = []
        value_nodes = []
        for key_node, value_node in zip(node.keys, node.values):
            if isinstance(value_node, ast.Subscript):
                key_nodes.append(ast.Str(s=f"{key_node.s}.$", ctx=ast.Load()))
                value_nodes.append(
                    ast.Str(s=convert_input_data_ref(value_node)))
            else:
                key_nodes.append(self.generic_visit(key_node))
                value_nodes.append(self.generic_visit(value_node))

        return ast.copy_location(
            ast.Dict(keys=key_nodes, values=value_nodes, ctx=ast.Load()), node)
Esempio n. 4
0
    def _get_input_path(self) -> str:
        """Get the InputPath value for the map state

        The input path should point to an object with keys:
        * **key** -- key pointing to the collection of state data items stored in
          DynamoDB. This key would have been generated in an earlier task.
        * **items** -- list of items that will be fanned-out. The length of the list is
          more important than the contents of each item because we'll use the ``key``
          to fetch the actual item value from DynamoDB.

        Returns:
            input path string

        """
        if len(self.ast_node.value.args) > 0:
            return convert_input_data_ref(self.ast_node.value.args[0])

        return "$"
Esempio n. 5
0
    def _parse_result_path(self) -> str:
        """Parse the result path from the AST node.

        The result path is where the result value will be inserted into the data object.

        Returns:
            result path string

        """
        if isinstance(self.ast_node, ast.Assign):
            result_path = convert_input_data_ref(self.ast_node.targets[0])
            assert_supported_operation(
                re.search(INVALID_RESULT_PATH_PATTERN, result_path) is None,
                "Task result path is invalid. Check that it does not contain reserved"
                f" keys: {', '.join(RESERVED_INPUT_DATA_KEYS)}",
                self.ast_node,
            )
            return result_path

        return "$"
Esempio n. 6
0
    def _get_result_path(self) -> Optional[str]:
        """Get the ResultPath value for the task state

        If the result path was not explicitly provided, return None to indicate that
        the the result should be discarded.

        Returns:
            result path string or None

        """
        if isinstance(self.ast_node,
                      ast.Assign) and len(self.ast_node.targets) > 0:
            result_path = convert_input_data_ref(self.ast_node.targets[0])
            assert_supported_operation(
                re.search(INVALID_RESULT_PATH_PATTERN, result_path) is None,
                "Task result path is invalid. Check that it does not contain reserved"
                f" keys: {', '.join(RESERVED_INPUT_DATA_KEYS)}",
                self.ast_node,
            )
            return result_path

        return None
 def visit_Subscript(self, node: Any) -> None:
     """Visit a subscript"""
     return ast.copy_location(
         ast.Str(s=convert_input_data_ref(node), ctx=ast.Load()), node)
Esempio n. 8
0
    def _serialize(self, node: Any) -> Any:
        """Recursive function to serialize a part of the choice branch AST node.

        This looks at each part of the conditional statement's AST to determine the
        correct ASL representation.
        """
        if isinstance(node, ast.BoolOp):
            # e.g. ``___ and ___``
            return {
                OP_TO_KEY[node.op.__class__]: [
                    self._serialize(value) for value in node.values
                ]
            }
        elif isinstance(node, ast.UnaryOp) and isinstance(node.op, ast.Not):
            # e.g. ``not bool(data["foo"])``
            if isinstance(node.operand, ast.Call) and node.operand.func.id == "bool":
                value = {
                    "Variable": self._serialize(node.operand),
                    OP_TO_KEY[(ast.Eq, bool)]: True,
                }
            else:
                value = self._serialize(node.operand)
            return {"Not": value}
        elif isinstance(node, ast.Compare):
            # e.g. ``data["foo"] > 0``
            assert_supported_operation(
                len(node.ops) == 1,
                "Only 1 comparison operator at a time is allowed",
                node,
            )
            assert_supported_operation(
                len(node.comparators) == 1,
                "Only 1 comparator at a time is allowed",
                node,
            )
            op = node.ops[0]
            comparator = node.comparators[0]
            # Determine the data type of the choice
            type_ = None
            if isinstance(node.left, ast.Call):
                type_ = node.left.func.id
            if isinstance(comparator, ast.Call):
                assert_supported_operation(
                    type_ is None
                    or (type_ is not None and comparator.func.id == type_),
                    f"Value types must match. Found: {type_} {op.__class__}"
                    f" {comparator.func.id}",
                    node,
                )
                if type_ is None:
                    type_ = comparator.func.id
            elif isinstance(comparator, ast.Str):
                type_ = "str"
            elif isinstance(comparator, ast.Num):
                type_ = "float"
            elif isinstance(comparator, ast.NameConstant) and comparator.value in (
                True,
                False,
            ):
                type_ = "bool"
            elif isinstance(comparator, ast.Subscript):
                raise UnsupportedOperation(
                    "Input data cannot be used as the comparator (right side of operation)",
                    comparator,
                )
            else:
                raise UnsupportedOperation(
                    "Could not determine data type for choice variable", comparator
                )

            type_class = TYPE_TO_CLASS[type_]
            if isinstance(op, ast.NotEq):
                return {
                    "Not": {
                        "Variable": self._serialize(node.left),
                        OP_TO_KEY[(ast.Eq, type_class)]: self._serialize(comparator),
                    }
                }

            return {
                "Variable": self._serialize(node.left),
                OP_TO_KEY[(op.__class__, type_class)]: self._serialize(comparator),
            }

        elif isinstance(node, ast.Subscript):
            # e.g. ``data["foo"]``
            return convert_input_data_ref(node)
        elif isinstance(node, ast.Call):
            # e.g. ``int(data["foo"])``
            assert_supported_operation(
                node.func.id in TYPE_TO_CLASS,
                f"Function {node.func.id} is not supported. Allowed built-ins: "
                ", ".join(TYPE_TO_CLASS.keys()),
                node,
            )
            assert_supported_operation(
                len(node.args) == 1,
                "Data type casting functions only accept 1 positional argument",
                node,
            )
            if node.func.id == "bool":
                return {
                    "Variable": self._serialize(node.args[0]),
                    OP_TO_KEY[(ast.Eq, bool)]: True,
                }
            else:
                return self._serialize(node.args[0])
        elif isinstance(node, ast.NameConstant):
            assert_supported_operation(
                node.value is not None,
                "The value `None` is not allowed in Choice states",
                node,
            )
            return node.value
        elif isinstance(node, ast.Str):
            return node.s
        elif isinstance(node, ast.Num):
            return node.n

        raise UnsupportedOperation("Unsupported choice branch logic", node)