From 1fe128f7b510e125ce418cd2bc8d762f045437bf Mon Sep 17 00:00:00 2001 From: Vadim Levin Date: Wed, 10 Sep 2025 11:25:32 +0300 Subject: [PATCH] feat: add PathLike type hints for args annotated with CV_WRAP_FILE_PATH --- .../src2/typing_stubs_generation/__init__.py | 1 + .../src2/typing_stubs_generation/ast_utils.py | 8 +++-- .../typing_stubs_generation/nodes/__init__.py | 2 +- .../nodes/type_node.py | 36 +++++++++++++++---- 4 files changed, 38 insertions(+), 9 deletions(-) diff --git a/modules/python/src2/typing_stubs_generation/__init__.py b/modules/python/src2/typing_stubs_generation/__init__.py index 20ebf5773f..c0ae42ce76 100644 --- a/modules/python/src2/typing_stubs_generation/__init__.py +++ b/modules/python/src2/typing_stubs_generation/__init__.py @@ -12,6 +12,7 @@ from .nodes import ( SequenceTypeNode, AnyTypeNode, AggregatedTypeNode, + PathLikeTypeNode, ) from .types_conversion import ( diff --git a/modules/python/src2/typing_stubs_generation/ast_utils.py b/modules/python/src2/typing_stubs_generation/ast_utils.py index e8ef52d19a..27f5e470d2 100644 --- a/modules/python/src2/typing_stubs_generation/ast_utils.py +++ b/modules/python/src2/typing_stubs_generation/ast_utils.py @@ -4,7 +4,7 @@ import keyword from .nodes import (ASTNode, NamespaceNode, ClassNode, FunctionNode, EnumerationNode, ClassProperty, OptionalTypeNode, - TupleTypeNode) + TupleTypeNode, PathLikeTypeNode) from .types_conversion import create_type_node @@ -167,12 +167,16 @@ def create_function_node_in_scope(scope: Union[NamespaceNode, ClassNode], func_info) -> FunctionNode: def prepare_overload_arguments_and_return_type(variant): arguments = [] # type: list[FunctionNode.Arg] - # Enumerate is requried, because `argno` in `variant.py_arglist` + # Enumerate is required, because `argno` in `variant.py_arglist` # refers to position of argument in C++ function interface, # but `variant.py_noptargs` refers to position in `py_arglist` for i, (_, argno) in enumerate(variant.py_arglist): arg_info = variant.args[argno] type_node = create_type_node(arg_info.tp) + # Special handling for string representation of the file system path + if arg_info.pathlike and type_node.typename == "str": + type_node = PathLikeTypeNode.string_or_pathlike_() + default_value = None if len(arg_info.defval): default_value = arg_info.defval diff --git a/modules/python/src2/typing_stubs_generation/nodes/__init__.py b/modules/python/src2/typing_stubs_generation/nodes/__init__.py index a2dbc49964..eb7769d415 100644 --- a/modules/python/src2/typing_stubs_generation/nodes/__init__.py +++ b/modules/python/src2/typing_stubs_generation/nodes/__init__.py @@ -8,5 +8,5 @@ from .type_node import ( TypeNode, OptionalTypeNode, UnionTypeNode, NoneTypeNode, TupleTypeNode, ASTNodeTypeNode, AliasTypeNode, SequenceTypeNode, AnyTypeNode, AggregatedTypeNode, NDArrayTypeNode, AliasRefTypeNode, PrimitiveTypeNode, - CallableTypeNode, DictTypeNode, ClassTypeNode + CallableTypeNode, DictTypeNode, ClassTypeNode, PathLikeTypeNode ) diff --git a/modules/python/src2/typing_stubs_generation/nodes/type_node.py b/modules/python/src2/typing_stubs_generation/nodes/type_node.py index a1b7ce5b46..86bd744a1c 100644 --- a/modules/python/src2/typing_stubs_generation/nodes/type_node.py +++ b/modules/python/src2/typing_stubs_generation/nodes/type_node.py @@ -43,7 +43,7 @@ class TypeNode(abc.ABC): Returns: str: short name of the type node. """ - pass + return "" @property def full_typename(self) -> str: @@ -123,12 +123,11 @@ class TypeNode(abc.ABC): def is_resolved(self) -> bool: return True - def relative_typename(self, module_full_export_name: str) -> str: + def relative_typename(self, module: str) -> str: """Type name relative to the provided module. Args: - module_full_export_name (str): Full export name of the module to - get relative name to. + module (str): Full export name of the module to get relative name to. Returns: str: If module name of the type node doesn't match `module`, then @@ -715,11 +714,11 @@ class ContainerTypeNode(AggregatedTypeNode): @abc.abstractproperty def type_format(self) -> str: - pass + return "" @abc.abstractproperty def types_separator(self) -> str: - pass + return "" class SequenceTypeNode(ContainerTypeNode): @@ -895,6 +894,31 @@ class ClassTypeNode(ContainerTypeNode): return ", " +class PathLikeTypeNode(TypeNode): + """Type node representing a PathLike object. + """ + def __init__(self, ctype_name: str) -> None: + super().__init__(ctype_name) + + @property + def typename(self) -> str: + return "os.PathLike[str]" + + @property + def required_usage_imports(self) -> Generator[str, None, None]: + yield "import os" + + @staticmethod + def string_or_pathlike_(ctype_name: str = "string") -> UnionTypeNode: + return UnionTypeNode( + ctype_name, + items=( + PrimitiveTypeNode.str_(ctype_name), + PathLikeTypeNode(ctype_name) + ) + ) + + def _resolve_symbol(root: Optional[ASTNode], full_symbol_name: str) -> Optional[ASTNode]: """Searches for a symbol with the given full export name in the AST starting from the `root`.