Merge pull request #26527 from asmorkalov:as/gapi_migration

Restore predefined G-API types in Python type stubs generator
This commit is contained in:
Alexander Smorkalov 2024-11-27 16:01:02 +03:00 committed by GitHub
commit e1ba8f2659
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
3 changed files with 152 additions and 42 deletions

View File

@ -706,6 +706,10 @@ def _generate_typing_module(root: NamespaceNode, output_path: Path) -> None:
output_path (Path): Path to typing module directory, where __init__.pyi output_path (Path): Path to typing module directory, where __init__.pyi
will be written. will be written.
""" """
def has_all_required_modules(type_node: TypeNode) -> bool:
return all(em in root.namespaces for em in node.required_modules)
def register_alias_links_from_aggregated_type(type_node: TypeNode) -> None: def register_alias_links_from_aggregated_type(type_node: TypeNode) -> None:
assert isinstance(type_node, AggregatedTypeNode), \ assert isinstance(type_node, AggregatedTypeNode), \
f"Provided type node '{type_node.ctype_name}' is not an aggregated type" f"Provided type node '{type_node.ctype_name}' is not an aggregated type"
@ -791,6 +795,10 @@ def _generate_typing_module(root: NamespaceNode, output_path: Path) -> None:
# Resolve each node and register aliases # Resolve each node and register aliases
TypeNode.compatible_to_runtime_usage = True TypeNode.compatible_to_runtime_usage = True
for node in PREDEFINED_TYPES.values(): for node in PREDEFINED_TYPES.values():
# if node does not have at least one required module skip it
# e.g. GArgs requires G-API module, so if build without G-API GArgs is not included
if not has_all_required_modules(node):
continue
node.resolve(root) node.resolve(root)
if isinstance(node, AliasTypeNode): if isinstance(node, AliasTypeNode):
register_alias(node) register_alias(node)

View File

@ -1,6 +1,7 @@
from typing import Sequence, Generator, Tuple, Optional, Union from typing import Sequence, Generator, Tuple, Optional, Union
import weakref import weakref
import abc import abc
from itertools import chain
from .node import ASTNode, ASTNodeType from .node import ASTNode, ASTNodeType
@ -30,8 +31,9 @@ class TypeNode(abc.ABC):
"typing.Optional[Size]" "typing.Optional[Size]"
""" """
def __init__(self, ctype_name: str) -> None: def __init__(self, ctype_name: str, required_modules: Tuple[str, ...] = ()) -> None:
self.ctype_name = ctype_name self.ctype_name = ctype_name
self._required_modules = required_modules
@abc.abstractproperty @abc.abstractproperty
def typename(self) -> str: def typename(self) -> str:
@ -113,6 +115,10 @@ class TypeNode(abc.ABC):
""" """
yield from () yield from ()
@property
def required_modules(self) -> Tuple[str, ...]:
return self._required_modules
@property @property
def is_resolved(self) -> bool: def is_resolved(self) -> bool:
return True return True
@ -224,8 +230,9 @@ class AliasRefTypeNode(TypeNode):
``` ```
""" """
def __init__(self, alias_ctype_name: str, def __init__(self, alias_ctype_name: str,
alias_export_name: Optional[str] = None): alias_export_name: Optional[str] = None,
super().__init__(alias_ctype_name) required_modules: Tuple[str, ...] = ()):
super().__init__(alias_ctype_name, required_modules)
if alias_export_name is None: if alias_export_name is None:
self.alias_export_name = alias_ctype_name self.alias_export_name = alias_ctype_name
else: else:
@ -258,8 +265,9 @@ class AliasTypeNode(TypeNode):
""" """
def __init__(self, ctype_name: str, value: TypeNode, def __init__(self, ctype_name: str, value: TypeNode,
export_name: Optional[str] = None, export_name: Optional[str] = None,
doc: Optional[str] = None) -> None: doc: Optional[str] = None,
super().__init__(ctype_name) required_modules: Tuple[str, ...] = ()) -> None:
super().__init__(ctype_name, required_modules)
self.value = value self.value = value
self._export_name = export_name self._export_name = export_name
self.doc = doc self.doc = doc
@ -298,20 +306,21 @@ class AliasTypeNode(TypeNode):
@classmethod @classmethod
def int_(cls, ctype_name: str, export_name: Optional[str] = None, def int_(cls, ctype_name: str, export_name: Optional[str] = None,
doc: Optional[str] = None): doc: Optional[str] = None, required_modules: Tuple[str, ...] = ()):
return cls(ctype_name, PrimitiveTypeNode.int_(), export_name, doc) return cls(ctype_name, PrimitiveTypeNode.int_(), export_name, doc, required_modules)
@classmethod @classmethod
def float_(cls, ctype_name: str, export_name: Optional[str] = None, def float_(cls, ctype_name: str, export_name: Optional[str] = None,
doc: Optional[str] = None): doc: Optional[str] = None, required_modules: Tuple[str, ...] = ()):
return cls(ctype_name, PrimitiveTypeNode.float_(), export_name, doc) return cls(ctype_name, PrimitiveTypeNode.float_(), export_name, doc, required_modules)
@classmethod @classmethod
def array_ref_(cls, ctype_name: str, array_ref_name: str, def array_ref_(cls, ctype_name: str, array_ref_name: str,
shape: Optional[Tuple[int, ...]], shape: Optional[Tuple[int, ...]],
dtype: Optional[str] = None, dtype: Optional[str] = None,
export_name: Optional[str] = None, export_name: Optional[str] = None,
doc: Optional[str] = None): doc: Optional[str] = None,
required_modules: Tuple[str, ...] = ()):
"""Create alias to array reference alias `array_ref_name`. """Create alias to array reference alias `array_ref_name`.
This is required to preserve backward compatibility with Python < 3.9 This is required to preserve backward compatibility with Python < 3.9
@ -332,65 +341,74 @@ class AliasTypeNode(TypeNode):
else: else:
doc += f". NDArray(shape={shape}, dtype={dtype})" doc += f". NDArray(shape={shape}, dtype={dtype})"
return cls(ctype_name, AliasRefTypeNode(array_ref_name), return cls(ctype_name, AliasRefTypeNode(array_ref_name),
export_name, doc) export_name, doc, required_modules)
@classmethod @classmethod
def union_(cls, ctype_name: str, items: Tuple[TypeNode, ...], def union_(cls, ctype_name: str, items: Tuple[TypeNode, ...],
export_name: Optional[str] = None, export_name: Optional[str] = None,
doc: Optional[str] = None): doc: Optional[str] = None,
required_modules: Tuple[str, ...] = ()):
return cls(ctype_name, UnionTypeNode(ctype_name, items), return cls(ctype_name, UnionTypeNode(ctype_name, items),
export_name, doc) export_name, doc, required_modules)
@classmethod @classmethod
def optional_(cls, ctype_name: str, item: TypeNode, def optional_(cls, ctype_name: str, item: TypeNode,
export_name: Optional[str] = None, export_name: Optional[str] = None,
doc: Optional[str] = None): doc: Optional[str] = None,
return cls(ctype_name, OptionalTypeNode(item), export_name, doc) required_modules: Tuple[str, ...] = ()):
return cls(ctype_name, OptionalTypeNode(item), export_name, doc, required_modules)
@classmethod @classmethod
def sequence_(cls, ctype_name: str, item: TypeNode, def sequence_(cls, ctype_name: str, item: TypeNode,
export_name: Optional[str] = None, export_name: Optional[str] = None,
doc: Optional[str] = None): doc: Optional[str] = None,
required_modules: Tuple[str, ...] = ()):
return cls(ctype_name, SequenceTypeNode(ctype_name, item), return cls(ctype_name, SequenceTypeNode(ctype_name, item),
export_name, doc) export_name, doc, required_modules)
@classmethod @classmethod
def tuple_(cls, ctype_name: str, items: Tuple[TypeNode, ...], def tuple_(cls, ctype_name: str, items: Tuple[TypeNode, ...],
export_name: Optional[str] = None, export_name: Optional[str] = None,
doc: Optional[str] = None): doc: Optional[str] = None,
required_modules: Tuple[str, ...] = ()):
return cls(ctype_name, TupleTypeNode(ctype_name, items), return cls(ctype_name, TupleTypeNode(ctype_name, items),
export_name, doc) export_name, doc, required_modules)
@classmethod @classmethod
def class_(cls, ctype_name: str, class_name: str, def class_(cls, ctype_name: str, class_name: str,
export_name: Optional[str] = None, export_name: Optional[str] = None,
doc: Optional[str] = None): doc: Optional[str] = None,
required_modules: Tuple[str, ...] = ()):
return cls(ctype_name, ASTNodeTypeNode(class_name), return cls(ctype_name, ASTNodeTypeNode(class_name),
export_name, doc) export_name, doc, required_modules)
@classmethod @classmethod
def callable_(cls, ctype_name: str, def callable_(cls, ctype_name: str,
arg_types: Union[TypeNode, Sequence[TypeNode]], arg_types: Union[TypeNode, Sequence[TypeNode]],
ret_type: TypeNode = NoneTypeNode("void"), ret_type: TypeNode = NoneTypeNode("void"),
export_name: Optional[str] = None, export_name: Optional[str] = None,
doc: Optional[str] = None): doc: Optional[str] = None,
required_modules: Tuple[str, ...] = ()):
return cls(ctype_name, return cls(ctype_name,
CallableTypeNode(ctype_name, arg_types, ret_type), CallableTypeNode(ctype_name, arg_types, ret_type),
export_name, doc) export_name, doc, required_modules)
@classmethod @classmethod
def ref_(cls, ctype_name: str, alias_ctype_name: str, def ref_(cls, ctype_name: str, alias_ctype_name: str,
alias_export_name: Optional[str] = None, alias_export_name: Optional[str] = None,
export_name: Optional[str] = None, doc: Optional[str] = None): export_name: Optional[str] = None,
doc: Optional[str] = None,
required_modules: Tuple[str, ...] = ()):
return cls(ctype_name, return cls(ctype_name,
AliasRefTypeNode(alias_ctype_name, alias_export_name), AliasRefTypeNode(alias_ctype_name, alias_export_name),
export_name, doc) export_name, doc, required_modules)
@classmethod @classmethod
def dict_(cls, ctype_name: str, key_type: TypeNode, value_type: TypeNode, def dict_(cls, ctype_name: str, key_type: TypeNode, value_type: TypeNode,
export_name: Optional[str] = None, doc: Optional[str] = None): export_name: Optional[str] = None, doc: Optional[str] = None,
required_modules: Tuple[str, ...] = ()):
return cls(ctype_name, DictTypeNode(ctype_name, key_type, value_type), return cls(ctype_name, DictTypeNode(ctype_name, key_type, value_type),
export_name, doc) export_name, doc, required_modules)
class ConditionalAliasTypeNode(TypeNode): class ConditionalAliasTypeNode(TypeNode):
@ -451,6 +469,11 @@ class ConditionalAliasTypeNode(TypeNode):
def required_usage_imports(self) -> Generator[str, None, None]: def required_usage_imports(self) -> Generator[str, None, None]:
yield "import cv2.typing" yield "import cv2.typing"
@property
def required_modules(self) -> Tuple[str, ...]:
return (*self.positive_branch_type.required_modules,
*self.negative_branch_type.required_modules)
@property @property
def is_resolved(self) -> bool: def is_resolved(self) -> bool:
return self.positive_branch_type.is_resolved \ return self.positive_branch_type.is_resolved \
@ -519,8 +542,9 @@ class ASTNodeTypeNode(TypeNode):
too expensive and error prone. too expensive and error prone.
""" """
def __init__(self, ctype_name: str, typename: Optional[str] = None, def __init__(self, ctype_name: str, typename: Optional[str] = None,
module_name: Optional[str] = None) -> None: module_name: Optional[str] = None,
super().__init__(ctype_name) required_modules: Tuple[str, ...] = ()) -> None:
super().__init__(ctype_name, required_modules)
self._typename = typename if typename is not None else ctype_name self._typename = typename if typename is not None else ctype_name
self._module_name = module_name self._module_name = module_name
self._ast_node: Optional[weakref.ProxyType[ASTNode]] = None self._ast_node: Optional[weakref.ProxyType[ASTNode]] = None
@ -607,14 +631,20 @@ class ASTNodeTypeNode(TypeNode):
class AggregatedTypeNode(TypeNode): class AggregatedTypeNode(TypeNode):
"""Base type node for type nodes representing an aggregation of another """Base type node for type nodes representing an aggregation of another
type nodes e.g. tuple, sequence or callable.""" type nodes e.g. tuple, sequence or callable."""
def __init__(self, ctype_name: str, items: Sequence[TypeNode]) -> None: def __init__(self, ctype_name: str, items: Sequence[TypeNode],
super().__init__(ctype_name) required_modules: Tuple[str, ...] = ()) -> None:
super().__init__(ctype_name, required_modules)
self.items = list(items) self.items = list(items)
@property @property
def is_resolved(self) -> bool: def is_resolved(self) -> bool:
return all(item.is_resolved for item in self.items) return all(item.is_resolved for item in self.items)
@property
def required_modules(self) -> Tuple[str, ...]:
return (*chain.from_iterable(item.required_modules for item in self.items),
*self._required_modules)
def resolve(self, root: ASTNode) -> None: def resolve(self, root: ASTNode) -> None:
errors = [] errors = []
for item in filter(lambda item: not item.is_resolved, self): for item in filter(lambda item: not item.is_resolved, self):
@ -690,8 +720,9 @@ class SequenceTypeNode(ContainerTypeNode):
"""Type node representing a homogeneous collection of elements with """Type node representing a homogeneous collection of elements with
possible unknown length. possible unknown length.
""" """
def __init__(self, ctype_name: str, item: TypeNode) -> None: def __init__(self, ctype_name: str, item: TypeNode,
super().__init__(ctype_name, (item, )) required_modules: Tuple[str, ...] = ()) -> None:
super().__init__(ctype_name, (item, ), required_modules)
@property @property
def type_format(self) -> str: def type_format(self) -> str:
@ -737,8 +768,9 @@ class OptionalTypeNode(ContainerTypeNode):
"""Type node representing optional type which is effectively is a union """Type node representing optional type which is effectively is a union
of value type node and None. of value type node and None.
""" """
def __init__(self, value: TypeNode) -> None: def __init__(self, value: TypeNode,
super().__init__(value.ctype_name, (value,)) required_modules: Tuple[str, ...] = ()) -> None:
super().__init__(value.ctype_name, (value,), required_modules)
@property @property
def type_format(self) -> str: def type_format(self) -> str:
@ -755,8 +787,9 @@ class DictTypeNode(ContainerTypeNode):
"""Type node representing a homogeneous key-value mapping. """Type node representing a homogeneous key-value mapping.
""" """
def __init__(self, ctype_name: str, key_type: TypeNode, def __init__(self, ctype_name: str, key_type: TypeNode,
value_type: TypeNode) -> None: value_type: TypeNode,
super().__init__(ctype_name, (key_type, value_type)) required_modules: Tuple[str, ...] = ()) -> None:
super().__init__(ctype_name, (key_type, value_type), required_modules)
@property @property
def key_type(self) -> TypeNode: def key_type(self) -> TypeNode:
@ -794,11 +827,12 @@ class CallableTypeNode(AggregatedTypeNode):
""" """
def __init__(self, ctype_name: str, def __init__(self, ctype_name: str,
arg_types: Union[TypeNode, Sequence[TypeNode]], arg_types: Union[TypeNode, Sequence[TypeNode]],
ret_type: TypeNode = NoneTypeNode("void")) -> None: ret_type: TypeNode = NoneTypeNode("void"),
required_modules: Tuple[str, ...] = ()) -> None:
if isinstance(arg_types, TypeNode): if isinstance(arg_types, TypeNode):
super().__init__(ctype_name, (arg_types, ret_type)) super().__init__(ctype_name, (arg_types, ret_type), required_modules)
else: else:
super().__init__(ctype_name, (*arg_types, ret_type)) super().__init__(ctype_name, (*arg_types, ret_type), required_modules)
@property @property
def arg_types(self) -> Sequence[TypeNode]: def arg_types(self) -> Sequence[TypeNode]:
@ -842,8 +876,9 @@ class CallableTypeNode(AggregatedTypeNode):
class ClassTypeNode(ContainerTypeNode): class ClassTypeNode(ContainerTypeNode):
"""Type node representing types themselves (refer to typing.Type) """Type node representing types themselves (refer to typing.Type)
""" """
def __init__(self, value: TypeNode) -> None: def __init__(self, value: TypeNode,
super().__init__(value.ctype_name, (value,)) required_modules: Tuple[str, ...] = ()) -> None:
super().__init__(value.ctype_name, (value,), required_modules)
@property @property
def type_format(self) -> str: def type_format(self) -> str:

View File

@ -174,6 +174,73 @@ _PREDEFINED_TYPES = (
SequenceTypeNode("map_string_and_vector_float::value", PrimitiveTypeNode.float_())), SequenceTypeNode("map_string_and_vector_float::value", PrimitiveTypeNode.float_())),
AliasTypeNode.dict_("map_int_and_double", PrimitiveTypeNode.int_("map_int_and_double::key"), AliasTypeNode.dict_("map_int_and_double", PrimitiveTypeNode.int_("map_int_and_double::key"),
PrimitiveTypeNode.float_("map_int_and_double::value")), PrimitiveTypeNode.float_("map_int_and_double::value")),
# G-API from opencv_contrib
AliasTypeNode.union_("GProtoArg",
items=(AliasRefTypeNode("Scalar"),
ASTNodeTypeNode("GMat"),
ASTNodeTypeNode("GOpaqueT"),
ASTNodeTypeNode("GArrayT")),
required_modules=("gapi",)),
SequenceTypeNode("GProtoArgs", AliasRefTypeNode("GProtoArg"), required_modules=("gapi",)),
AliasTypeNode.sequence_("GProtoInputArgs", AliasRefTypeNode("GProtoArg"), required_modules=("gapi",)),
AliasTypeNode.sequence_("GProtoOutputArgs", AliasRefTypeNode("GProtoArg"), required_modules=("gapi",)),
AliasTypeNode.union_(
"GRunArg",
items=(AliasRefTypeNode("Mat", "MatLike"),
AliasRefTypeNode("Scalar"),
ASTNodeTypeNode("GOpaqueT"),
ASTNodeTypeNode("GArrayT"),
SequenceTypeNode("GRunArg", AnyTypeNode("GRunArg")),
NoneTypeNode("GRunArg")),
required_modules=("gapi",)
),
AliasTypeNode.optional_("GOptRunArg", AliasRefTypeNode("GRunArg"), required_modules=("gapi",)),
AliasTypeNode.union_("GMetaArg",
items=(ASTNodeTypeNode("GMat"),
AliasRefTypeNode("Scalar"),
ASTNodeTypeNode("GOpaqueT"),
ASTNodeTypeNode("GArrayT")),
required_modules=("gapi",)),
AliasTypeNode.union_("Prim",
items=(ASTNodeTypeNode("gapi.wip.draw.Text"),
ASTNodeTypeNode("gapi.wip.draw.Circle"),
ASTNodeTypeNode("gapi.wip.draw.Image"),
ASTNodeTypeNode("gapi.wip.draw.Line"),
ASTNodeTypeNode("gapi.wip.draw.Rect"),
ASTNodeTypeNode("gapi.wip.draw.Mosaic"),
ASTNodeTypeNode("gapi.wip.draw.Poly")),
required_modules=("gapi",)),
SequenceTypeNode("Prims", AliasRefTypeNode("Prim"), required_modules=("gapi",)),
TupleTypeNode("GMat2", items=(ASTNodeTypeNode("GMat"),
ASTNodeTypeNode("GMat")), required_modules=("gapi",)),
ASTNodeTypeNode("GOpaque", "GOpaqueT", required_modules=("gapi",)),
ASTNodeTypeNode("GArray", "GArrayT", required_modules=("gapi",)),
AliasTypeNode.union_("GTypeInfo",
items=(ASTNodeTypeNode("GMat"),
AliasRefTypeNode("Scalar"),
ASTNodeTypeNode("GOpaqueT"),
ASTNodeTypeNode("GArrayT")),
required_modules=("gapi",)),
SequenceTypeNode("GCompileArgs", ASTNodeTypeNode("GCompileArg"), required_modules=("gapi",)),
SequenceTypeNode("GTypesInfo", AliasRefTypeNode("GTypeInfo"), required_modules=("gapi",)),
SequenceTypeNode("GRunArgs", AliasRefTypeNode("GRunArg"), required_modules=("gapi",)),
SequenceTypeNode("GMetaArgs", AliasRefTypeNode("GMetaArg"), required_modules=("gapi",)),
SequenceTypeNode("GOptRunArgs", AliasRefTypeNode("GOptRunArg"), required_modules=("gapi",)),
AliasTypeNode.callable_(
"detail_ExtractArgsCallback",
arg_types=SequenceTypeNode("GTypesInfo", AliasRefTypeNode("GTypeInfo")),
ret_type=SequenceTypeNode("GRunArgs", AliasRefTypeNode("GRunArg")),
export_name="ExtractArgsCallback",
required_modules=("gapi",)
),
AliasTypeNode.callable_(
"detail_ExtractMetaCallback",
arg_types=SequenceTypeNode("GTypesInfo", AliasRefTypeNode("GTypeInfo")),
ret_type=SequenceTypeNode("GMetaArgs", AliasRefTypeNode("GMetaArg")),
export_name="ExtractMetaCallback",
required_modules=("gapi",)
),
) )
PREDEFINED_TYPES = dict( PREDEFINED_TYPES = dict(