Repository: opencv/opencv
File: modules/python/src2/typing_stubs_generation/api_refinement.py (Python)

Code before:
        root.add_constant(depth_name, str(depth_value))
        # Export predefined types
        for c in range(MAX_PREDEFINED_CHANNELS):
            root.add_constant(f"{depth_name}C{c + 1}",
                              f"{depth_value + 8 * c}")
        # Export type creation function
        root.add_function(
            f"{depth_name}C",
            (FunctionNode.Arg("channels", PrimitiveTypeNode.int_()), ),
            FunctionNode.RetType(PrimitiveTypeNode.int_())
        )
    # Export CV_MAKETYPE
    root.add_function(
        "CV_MAKETYPE",
        (FunctionNode.Arg("depth", PrimitiveTypeNode.int_()),
         FunctionNode.Arg("channels", PrimitiveTypeNode.int_())),
        FunctionNode.RetType(PrimitiveTypeNode.int_())
    )


def make_optional_arg(*arg_names: str) -> Callable[[NamespaceNode, SymbolName], None]:
    def _make_optional_arg(root_node: NamespaceNode,
                           function_symbol_name: SymbolName) -> None:
        function = find_function_node(root_node, function_symbol_name)
        for overload in function.overloads:
            for arg_name in arg_names:
                try:
                    arg_idx = _find_argument_index(overload.arguments, arg_name)
                except RuntimeError:
                    # skip overloads without this argument
                    continue

                # Avoid multiplying optional qualification
                if isinstance(overload.arguments[arg_idx].type_node, OptionalTypeNode):
                    continue

                overload.arguments[arg_idx].type_node = OptionalTypeNode(
                    cast(TypeNode, overload.arguments[arg_idx].type_node)
                )

    return _make_optional_arg


def refine_cuda_module(root: NamespaceNode) -> None:
    def fix_cudaoptflow_enums_names() -> None:
        for class_name in ("NvidiaOpticalFlow_1_0", "NvidiaOpticalFlow_2_0"):
            if class_name not in cuda_root.classes:
                continue
            opt_flow_class = cuda_root.classes[class_name]
            _trim_class_name_from_argument_types(
                for_each_function_overload(opt_flow_class), class_name

Review comment (suggestion):
It might sound controversial, but I would prefer outer loop over arguments rather than overloads.
Because it allows to add a check on the argument presence in at least 1 function overload instead of silently skip it (e.g. it will handle the "typo error" in argument name that is made optional).

Moreover, `_find_argument_index` should return `None` instead of rising the exception, because it is a valid case now.

Code after:
        root.add_constant(depth_name, str(depth_value))
        # Export predefined types
        for c in range(MAX_PREDEFINED_CHANNELS):
            root.add_constant(f"{depth_name}C{c + 1}",
                              f"{depth_value + 8 * c}")
        # Export type creation function
        root.add_function(
            f"{depth_name}C",
            (FunctionNode.Arg("channels", PrimitiveTypeNode.int_()), ),
            FunctionNode.RetType(PrimitiveTypeNode.int_())
        )
    # Export CV_MAKETYPE
    root.add_function(
        "CV_MAKETYPE",
        (FunctionNode.Arg("depth", PrimitiveTypeNode.int_()),
         FunctionNode.Arg("channels", PrimitiveTypeNode.int_())),
        FunctionNode.RetType(PrimitiveTypeNode.int_())
    )


def make_optional_arg(*arg_names: str) -> Callable[[NamespaceNode, SymbolName], None]:
    def _make_optional_arg(root_node: NamespaceNode,
                           function_symbol_name: SymbolName) -> None:
        function = find_function_node(root_node, function_symbol_name)
        for arg_name in arg_names:
            found_overload_with_arg = False

            for overload in function.overloads:
                arg_idx = _find_argument_index(overload.arguments, arg_name)

                # skip overloads without this argument
                if arg_idx is None:
                    continue

                # Avoid multiplying optional qualification
                if isinstance(overload.arguments[arg_idx].type_node, OptionalTypeNode):
                    continue

                overload.arguments[arg_idx].type_node = OptionalTypeNode(
                    cast(TypeNode, overload.arguments[arg_idx].type_node)
                )

                found_overload_with_arg = True

            if not found_overload_with_arg:
                raise RuntimeError(
                    f"Failed to find argument with name: '{arg_name}'"
                    f" in '{function_symbol_name.name}' overloads"
                )

    return _make_optional_arg