From f84705b45617ac7f93e663030dd0a92b78e8db5c Mon Sep 17 00:00:00 2001 From: Arsam Date: Fri, 3 May 2024 22:35:06 +0200 Subject: [PATCH 1/3] Added handling for named sequence types and removed a few "raises" and replaced them with UnknownType's --- src/safeds_stubgen/api_analyzer/__init__.py | 2 + .../api_analyzer/_ast_visitor.py | 7 +++ src/safeds_stubgen/api_analyzer/_types.py | 34 +++++++++++++++ .../docstring_parsing/_docstring_parser.py | 20 ++++++--- .../stubs_generator/_generate_stubs.py | 4 +- .../data/docstring_parser_package/numpydoc.py | 19 ++++++++ .../safeds_stubgen/api_analyzer/test_types.py | 43 +++++++++++++++++++ ...string_creation[numpydoc-NUMPYDOC].sdsstub | 13 ++++++ 8 files changed, 134 insertions(+), 8 deletions(-) diff --git a/src/safeds_stubgen/api_analyzer/__init__.py b/src/safeds_stubgen/api_analyzer/__init__.py index ac7e9894..ed1abdd4 100644 --- a/src/safeds_stubgen/api_analyzer/__init__.py +++ b/src/safeds_stubgen/api_analyzer/__init__.py @@ -29,6 +29,7 @@ FinalType, ListType, LiteralType, + NamedSequenceType, NamedType, SetType, TupleType, @@ -58,6 +59,7 @@ "ListType", "LiteralType", "Module", + "NamedSequenceType", "NamedType", "Parameter", "ParameterAssignment", diff --git a/src/safeds_stubgen/api_analyzer/_ast_visitor.py b/src/safeds_stubgen/api_analyzer/_ast_visitor.py index 7d2b553e..3fcdfccb 100644 --- a/src/safeds_stubgen/api_analyzer/_ast_visitor.py +++ b/src/safeds_stubgen/api_analyzer/_ast_visitor.py @@ -1,5 +1,6 @@ from __future__ import annotations +import logging from copy import deepcopy from types import NoneType from typing import TYPE_CHECKING @@ -941,6 +942,7 @@ def mypy_type_to_abstract_type( name, qname = self._find_alias(missing_import_name) if not qname: # pragma: no cover + logging.warning("Could not parse a type, added unknown type instead.") return sds_types.UnknownType() return sds_types.NamedType(name=name, qname=qname) @@ -978,6 +980,7 @@ def mypy_type_to_abstract_type( name, qname = self._find_alias(mypy_type.name) if not qname: # pragma: no cover + logging.warning("Could not parse a type, added unknown type instead.") return sds_types.UnknownType() return sds_types.NamedType(name=name, qname=qname) @@ -1009,8 +1012,12 @@ def mypy_type_to_abstract_type( value_type=self.mypy_type_to_abstract_type(mypy_type.args[1]), ) else: + if mypy_type.args: + types = [self.mypy_type_to_abstract_type(arg) for arg in mypy_type.args] + return sds_types.NamedSequenceType(name=type_name, qname=mypy_type.type.fullname, types=types) return sds_types.NamedType(name=type_name, qname=mypy_type.type.fullname) + logging.warning("Could not parse a type, added unknown type instead.") # pragma: no cover return sds_types.UnknownType() # pragma: no cover def _find_alias(self, type_name: str) -> tuple[str, str]: diff --git a/src/safeds_stubgen/api_analyzer/_types.py b/src/safeds_stubgen/api_analyzer/_types.py index b1a85511..57595af4 100644 --- a/src/safeds_stubgen/api_analyzer/_types.py +++ b/src/safeds_stubgen/api_analyzer/_types.py @@ -18,6 +18,8 @@ def from_dict(cls, d: dict[str, Any]) -> AbstractType: return UnknownType.from_dict(d) case NamedType.__name__: return NamedType.from_dict(d) + case NamedSequenceType.__name__: + return NamedSequenceType.from_dict(d) case EnumType.__name__: return EnumType.from_dict(d) case BoundaryType.__name__: @@ -83,6 +85,38 @@ def __hash__(self) -> int: return hash((self.name, self.qname)) +@dataclass(frozen=True) +class NamedSequenceType(AbstractType): + name: str + qname: str + types: Sequence[AbstractType] + + @classmethod + def from_dict(cls, d: dict[str, Any]) -> NamedSequenceType: + types = [] + for element in d["types"]: + type_ = AbstractType.from_dict(element) + if type_ is not None: + types.append(type_) + return NamedSequenceType(name=d["name"], qname=d["qname"], types=types) + + def to_dict(self) -> dict[str, Any]: + return { + "kind": self.__class__.__name__, + "name": self.name, + "qname": self.qname, + "types": [t.to_dict() for t in self.types], + } + + def __eq__(self, other: object) -> bool: + if not isinstance(other, NamedSequenceType): # pragma: no cover + return NotImplemented + return Counter(self.types) == Counter(other.types) and self.name == other.name and self.qname == other.qname + + def __hash__(self) -> int: + return hash(frozenset([self.name, self.qname, *self.types])) + + @dataclass(frozen=True) class EnumType(AbstractType): values: frozenset[str] = field(default_factory=frozenset) diff --git a/src/safeds_stubgen/docstring_parsing/_docstring_parser.py b/src/safeds_stubgen/docstring_parsing/_docstring_parser.py index 21d9b552..79c77372 100644 --- a/src/safeds_stubgen/docstring_parsing/_docstring_parser.py +++ b/src/safeds_stubgen/docstring_parsing/_docstring_parser.py @@ -1,5 +1,6 @@ from __future__ import annotations +import logging from typing import TYPE_CHECKING, Literal from griffe import load @@ -296,7 +297,9 @@ def _griffe_annotation_to_api_type( elif annotation.canonical_path in {"collections.abc.Callable", "typing.Callable"}: param_type = types[0] if len(types) >= 1 else [any_type] if not isinstance(param_type, sds_types.AbstractType): # pragma: no cover - raise TypeError(f"Expected AbstractType object, received {type(param_type)}") + msg = f"Expected AbstractType object, received {type(param_type)}. Added unknown type instead." + logging.warning(msg) + return sds_types.UnknownType() parameter_types = param_type.types if isinstance(param_type, sds_types.ListType) else [param_type] return_type = types[1] if len(types) >= 2 else any_type return sds_types.CallableType(parameter_types=parameter_types, return_type=return_type) @@ -307,8 +310,12 @@ def _griffe_annotation_to_api_type( elif annotation.canonical_path == "typing.Optional": types.append(sds_types.NamedType(name="None", qname="builtins.None")) return sds_types.UnionType(types=types) - else: # pragma: no cover - raise TypeError(f"Can't parse unexpected type from docstring {annotation.canonical_path}.") + else: + return sds_types.NamedSequenceType( + name=annotation.canonical_name, + qname=annotation.canonical_path, + types=types, + ) elif isinstance(annotation, ExprList): elements = [] for element in annotation.elements: @@ -363,10 +370,9 @@ def _griffe_annotation_to_api_type( types.append(left_type) return sds_types.UnionType(types=types) else: # pragma: no cover - raise TypeError( - f"Can't parse unexpected type from docstring: {annotation}. This case is not handled by us " - f"(yet), please report this.", - ) + msg = f"Can't parse unexpected type from docstring: {annotation}. Added unknown type instead." + logging.warning(msg) + return sds_types.UnknownType() def _remove_default_from_griffe_annotation(self, annotation: str) -> str: if self.parser == Parser.numpy: diff --git a/src/safeds_stubgen/stubs_generator/_generate_stubs.py b/src/safeds_stubgen/stubs_generator/_generate_stubs.py index 706be944..37b15d1c 100644 --- a/src/safeds_stubgen/stubs_generator/_generate_stubs.py +++ b/src/safeds_stubgen/stubs_generator/_generate_stubs.py @@ -737,7 +737,7 @@ def _create_type_string(self, type_data: dict | None) -> str: return_type_string = f"{result_name}: {self._create_type_string(return_type)}" return f"({', '.join(params)}) -> {return_type_string}" - elif kind in {"SetType", "ListType"}: + elif kind in {"SetType", "ListType", "NamedSequenceType"}: types = [self._create_type_string(type_) for type_ in type_data["types"]] # Cut out the "Type" in the kind name @@ -745,6 +745,8 @@ def _create_type_string(self, type_data: dict | None) -> str: if name == "Set": self._current_todo_msgs.add("no set support") + elif name == "NamedSequence": + name = type_data["name"] if types: if len(types) >= 2: diff --git a/tests/data/docstring_parser_package/numpydoc.py b/tests/data/docstring_parser_package/numpydoc.py index c965301c..1803c668 100644 --- a/tests/data/docstring_parser_package/numpydoc.py +++ b/tests/data/docstring_parser_package/numpydoc.py @@ -6,6 +6,7 @@ from typing import Any, Optional, Callable, Mapping from enum import Enum from tests.data.various_modules_package.another_path.another_module import AnotherClass +from tests.data.various_modules_package.type_var_module import SequenceTypeVar, SequenceTypeVar2 class ClassWithDocumentation: @@ -501,3 +502,21 @@ def numpy_func_with_examples(self): >>> func() This text should be ignored. """ + + +def numpy_sequence_types(a: SequenceTypeVar[list]) -> SequenceTypeVar2[int]: + """ + numpy_sequence_types. + + Dolor sit amet. + + Parameters + ---------- + a: SequenceTypeVar[list] + + Returns + ------- + named_result : SequenceTypeVar2[int] + this will be the return value + """ + pass diff --git a/tests/safeds_stubgen/api_analyzer/test_types.py b/tests/safeds_stubgen/api_analyzer/test_types.py index 5a8e3675..d0bce5e5 100644 --- a/tests/safeds_stubgen/api_analyzer/test_types.py +++ b/tests/safeds_stubgen/api_analyzer/test_types.py @@ -11,6 +11,7 @@ FinalType, ListType, LiteralType, + NamedSequenceType, NamedType, Parameter, ParameterAssignment, @@ -206,6 +207,48 @@ def test_list_type() -> None: assert ListType([NamedType("a", ""), NamedType("b", "")]) != ListType([NamedType("a", ""), NamedType("c", "")]) +def test_named_sequence_type() -> None: + list_type = NamedSequenceType("a", "b.a", [NamedType("str", "builtins.str"), NamedType("int", "builtins.int")]) + named_sequence_type_dict = { + "kind": "NamedSequenceType", + "name": "a", + "qname": "b.a", + "types": [ + {"kind": "NamedType", "name": "str", "qname": "builtins.str"}, + {"kind": "NamedType", "name": "int", "qname": "builtins.int"}, + ], + } + + assert AbstractType.from_dict(named_sequence_type_dict) == list_type + assert NamedSequenceType.from_dict(named_sequence_type_dict) == list_type + assert list_type.to_dict() == named_sequence_type_dict + + assert NamedSequenceType("a", "b.a", [NamedType("a", "")]) == NamedSequenceType("a", "b.a", [NamedType("a", "")]) + assert hash(NamedSequenceType("a", "b.a", [NamedType("a", "")])) == hash( + NamedSequenceType("a", "b.a", [NamedType("a", "")]), + ) + assert NamedSequenceType("a", "b.a", [NamedType("a", "")]) != NamedSequenceType("a", "b.a", [NamedType("b", "")]) + assert hash(NamedSequenceType("a", "b.a", [NamedType("a", "")])) != hash( + NamedSequenceType("a", "b.a", [NamedType("b", "")]), + ) + + assert NamedSequenceType("a", "b.a", [NamedType("a", ""), LiteralType(["b"])]) == NamedSequenceType( + "a", + "b.a", + [LiteralType(["b"]), NamedType("a", "")], + ) + assert NamedSequenceType("a", "b.a", [NamedType("a", ""), LiteralType(["b"])]) != NamedSequenceType( + "a", + "b.a", + [LiteralType(["a"]), NamedType("b", "")], + ) + assert NamedSequenceType("a", "b.a", [NamedType("a", ""), NamedType("b", "")]) != NamedSequenceType( + "a", + "b.a", + [NamedType("a", ""), NamedType("c", "")], + ) + + def test_dict_type() -> None: dict_type = DictType( key_type=UnionType([NamedType("str", "builtins.str"), NamedType("int", "builtins.int")]), diff --git a/tests/safeds_stubgen/stubs_generator/__snapshots__/test_generate_stubs/test_stub_docstring_creation[numpydoc-NUMPYDOC].sdsstub b/tests/safeds_stubgen/stubs_generator/__snapshots__/test_generate_stubs/test_stub_docstring_creation[numpydoc-NUMPYDOC].sdsstub index 35943ec0..e502dd12 100644 --- a/tests/safeds_stubgen/stubs_generator/__snapshots__/test_generate_stubs/test_stub_docstring_creation[numpydoc-NUMPYDOC].sdsstub +++ b/tests/safeds_stubgen/stubs_generator/__snapshots__/test_generate_stubs/test_stub_docstring_creation[numpydoc-NUMPYDOC].sdsstub @@ -162,6 +162,19 @@ fun inferTypes3( b ) -> result1: union +/** + * numpy_sequence_types. + * + * Dolor sit amet. + * + * @result namedResult this will be the return value + */ +@Pure +@PythonName("numpy_sequence_types") +fun numpySequenceTypes( + a: SequenceTypeVar> +) -> namedResult: SequenceTypeVar2 + /** * ClassWithDocumentation. Code:: * From 6842fbb081476f6d4fa6b3eba2bd1132e35d78f2 Mon Sep 17 00:00:00 2001 From: Arsam Date: Fri, 3 May 2024 22:35:06 +0200 Subject: [PATCH 2/3] Added handling for named sequence types and removed a few "raises" and replaced them with UnknownType's --- src/safeds_stubgen/api_analyzer/__init__.py | 2 + .../api_analyzer/_ast_visitor.py | 7 +++ src/safeds_stubgen/api_analyzer/_types.py | 34 +++++++++++++++ .../docstring_parsing/_docstring_parser.py | 20 ++++++--- .../stubs_generator/_generate_stubs.py | 4 +- .../data/docstring_parser_package/numpydoc.py | 19 ++++++++ .../safeds_stubgen/api_analyzer/test_types.py | 43 +++++++++++++++++++ ...string_creation[numpydoc-NUMPYDOC].sdsstub | 13 ++++++ 8 files changed, 134 insertions(+), 8 deletions(-) diff --git a/src/safeds_stubgen/api_analyzer/__init__.py b/src/safeds_stubgen/api_analyzer/__init__.py index ac7e9894..ed1abdd4 100644 --- a/src/safeds_stubgen/api_analyzer/__init__.py +++ b/src/safeds_stubgen/api_analyzer/__init__.py @@ -29,6 +29,7 @@ FinalType, ListType, LiteralType, + NamedSequenceType, NamedType, SetType, TupleType, @@ -58,6 +59,7 @@ "ListType", "LiteralType", "Module", + "NamedSequenceType", "NamedType", "Parameter", "ParameterAssignment", diff --git a/src/safeds_stubgen/api_analyzer/_ast_visitor.py b/src/safeds_stubgen/api_analyzer/_ast_visitor.py index 7d2b553e..3fcdfccb 100644 --- a/src/safeds_stubgen/api_analyzer/_ast_visitor.py +++ b/src/safeds_stubgen/api_analyzer/_ast_visitor.py @@ -1,5 +1,6 @@ from __future__ import annotations +import logging from copy import deepcopy from types import NoneType from typing import TYPE_CHECKING @@ -941,6 +942,7 @@ def mypy_type_to_abstract_type( name, qname = self._find_alias(missing_import_name) if not qname: # pragma: no cover + logging.warning("Could not parse a type, added unknown type instead.") return sds_types.UnknownType() return sds_types.NamedType(name=name, qname=qname) @@ -978,6 +980,7 @@ def mypy_type_to_abstract_type( name, qname = self._find_alias(mypy_type.name) if not qname: # pragma: no cover + logging.warning("Could not parse a type, added unknown type instead.") return sds_types.UnknownType() return sds_types.NamedType(name=name, qname=qname) @@ -1009,8 +1012,12 @@ def mypy_type_to_abstract_type( value_type=self.mypy_type_to_abstract_type(mypy_type.args[1]), ) else: + if mypy_type.args: + types = [self.mypy_type_to_abstract_type(arg) for arg in mypy_type.args] + return sds_types.NamedSequenceType(name=type_name, qname=mypy_type.type.fullname, types=types) return sds_types.NamedType(name=type_name, qname=mypy_type.type.fullname) + logging.warning("Could not parse a type, added unknown type instead.") # pragma: no cover return sds_types.UnknownType() # pragma: no cover def _find_alias(self, type_name: str) -> tuple[str, str]: diff --git a/src/safeds_stubgen/api_analyzer/_types.py b/src/safeds_stubgen/api_analyzer/_types.py index b1a85511..57595af4 100644 --- a/src/safeds_stubgen/api_analyzer/_types.py +++ b/src/safeds_stubgen/api_analyzer/_types.py @@ -18,6 +18,8 @@ def from_dict(cls, d: dict[str, Any]) -> AbstractType: return UnknownType.from_dict(d) case NamedType.__name__: return NamedType.from_dict(d) + case NamedSequenceType.__name__: + return NamedSequenceType.from_dict(d) case EnumType.__name__: return EnumType.from_dict(d) case BoundaryType.__name__: @@ -83,6 +85,38 @@ def __hash__(self) -> int: return hash((self.name, self.qname)) +@dataclass(frozen=True) +class NamedSequenceType(AbstractType): + name: str + qname: str + types: Sequence[AbstractType] + + @classmethod + def from_dict(cls, d: dict[str, Any]) -> NamedSequenceType: + types = [] + for element in d["types"]: + type_ = AbstractType.from_dict(element) + if type_ is not None: + types.append(type_) + return NamedSequenceType(name=d["name"], qname=d["qname"], types=types) + + def to_dict(self) -> dict[str, Any]: + return { + "kind": self.__class__.__name__, + "name": self.name, + "qname": self.qname, + "types": [t.to_dict() for t in self.types], + } + + def __eq__(self, other: object) -> bool: + if not isinstance(other, NamedSequenceType): # pragma: no cover + return NotImplemented + return Counter(self.types) == Counter(other.types) and self.name == other.name and self.qname == other.qname + + def __hash__(self) -> int: + return hash(frozenset([self.name, self.qname, *self.types])) + + @dataclass(frozen=True) class EnumType(AbstractType): values: frozenset[str] = field(default_factory=frozenset) diff --git a/src/safeds_stubgen/docstring_parsing/_docstring_parser.py b/src/safeds_stubgen/docstring_parsing/_docstring_parser.py index 21d9b552..79c77372 100644 --- a/src/safeds_stubgen/docstring_parsing/_docstring_parser.py +++ b/src/safeds_stubgen/docstring_parsing/_docstring_parser.py @@ -1,5 +1,6 @@ from __future__ import annotations +import logging from typing import TYPE_CHECKING, Literal from griffe import load @@ -296,7 +297,9 @@ def _griffe_annotation_to_api_type( elif annotation.canonical_path in {"collections.abc.Callable", "typing.Callable"}: param_type = types[0] if len(types) >= 1 else [any_type] if not isinstance(param_type, sds_types.AbstractType): # pragma: no cover - raise TypeError(f"Expected AbstractType object, received {type(param_type)}") + msg = f"Expected AbstractType object, received {type(param_type)}. Added unknown type instead." + logging.warning(msg) + return sds_types.UnknownType() parameter_types = param_type.types if isinstance(param_type, sds_types.ListType) else [param_type] return_type = types[1] if len(types) >= 2 else any_type return sds_types.CallableType(parameter_types=parameter_types, return_type=return_type) @@ -307,8 +310,12 @@ def _griffe_annotation_to_api_type( elif annotation.canonical_path == "typing.Optional": types.append(sds_types.NamedType(name="None", qname="builtins.None")) return sds_types.UnionType(types=types) - else: # pragma: no cover - raise TypeError(f"Can't parse unexpected type from docstring {annotation.canonical_path}.") + else: + return sds_types.NamedSequenceType( + name=annotation.canonical_name, + qname=annotation.canonical_path, + types=types, + ) elif isinstance(annotation, ExprList): elements = [] for element in annotation.elements: @@ -363,10 +370,9 @@ def _griffe_annotation_to_api_type( types.append(left_type) return sds_types.UnionType(types=types) else: # pragma: no cover - raise TypeError( - f"Can't parse unexpected type from docstring: {annotation}. This case is not handled by us " - f"(yet), please report this.", - ) + msg = f"Can't parse unexpected type from docstring: {annotation}. Added unknown type instead." + logging.warning(msg) + return sds_types.UnknownType() def _remove_default_from_griffe_annotation(self, annotation: str) -> str: if self.parser == Parser.numpy: diff --git a/src/safeds_stubgen/stubs_generator/_generate_stubs.py b/src/safeds_stubgen/stubs_generator/_generate_stubs.py index 706be944..37b15d1c 100644 --- a/src/safeds_stubgen/stubs_generator/_generate_stubs.py +++ b/src/safeds_stubgen/stubs_generator/_generate_stubs.py @@ -737,7 +737,7 @@ def _create_type_string(self, type_data: dict | None) -> str: return_type_string = f"{result_name}: {self._create_type_string(return_type)}" return f"({', '.join(params)}) -> {return_type_string}" - elif kind in {"SetType", "ListType"}: + elif kind in {"SetType", "ListType", "NamedSequenceType"}: types = [self._create_type_string(type_) for type_ in type_data["types"]] # Cut out the "Type" in the kind name @@ -745,6 +745,8 @@ def _create_type_string(self, type_data: dict | None) -> str: if name == "Set": self._current_todo_msgs.add("no set support") + elif name == "NamedSequence": + name = type_data["name"] if types: if len(types) >= 2: diff --git a/tests/data/docstring_parser_package/numpydoc.py b/tests/data/docstring_parser_package/numpydoc.py index c965301c..1803c668 100644 --- a/tests/data/docstring_parser_package/numpydoc.py +++ b/tests/data/docstring_parser_package/numpydoc.py @@ -6,6 +6,7 @@ from typing import Any, Optional, Callable, Mapping from enum import Enum from tests.data.various_modules_package.another_path.another_module import AnotherClass +from tests.data.various_modules_package.type_var_module import SequenceTypeVar, SequenceTypeVar2 class ClassWithDocumentation: @@ -501,3 +502,21 @@ def numpy_func_with_examples(self): >>> func() This text should be ignored. """ + + +def numpy_sequence_types(a: SequenceTypeVar[list]) -> SequenceTypeVar2[int]: + """ + numpy_sequence_types. + + Dolor sit amet. + + Parameters + ---------- + a: SequenceTypeVar[list] + + Returns + ------- + named_result : SequenceTypeVar2[int] + this will be the return value + """ + pass diff --git a/tests/safeds_stubgen/api_analyzer/test_types.py b/tests/safeds_stubgen/api_analyzer/test_types.py index 5a8e3675..d0bce5e5 100644 --- a/tests/safeds_stubgen/api_analyzer/test_types.py +++ b/tests/safeds_stubgen/api_analyzer/test_types.py @@ -11,6 +11,7 @@ FinalType, ListType, LiteralType, + NamedSequenceType, NamedType, Parameter, ParameterAssignment, @@ -206,6 +207,48 @@ def test_list_type() -> None: assert ListType([NamedType("a", ""), NamedType("b", "")]) != ListType([NamedType("a", ""), NamedType("c", "")]) +def test_named_sequence_type() -> None: + list_type = NamedSequenceType("a", "b.a", [NamedType("str", "builtins.str"), NamedType("int", "builtins.int")]) + named_sequence_type_dict = { + "kind": "NamedSequenceType", + "name": "a", + "qname": "b.a", + "types": [ + {"kind": "NamedType", "name": "str", "qname": "builtins.str"}, + {"kind": "NamedType", "name": "int", "qname": "builtins.int"}, + ], + } + + assert AbstractType.from_dict(named_sequence_type_dict) == list_type + assert NamedSequenceType.from_dict(named_sequence_type_dict) == list_type + assert list_type.to_dict() == named_sequence_type_dict + + assert NamedSequenceType("a", "b.a", [NamedType("a", "")]) == NamedSequenceType("a", "b.a", [NamedType("a", "")]) + assert hash(NamedSequenceType("a", "b.a", [NamedType("a", "")])) == hash( + NamedSequenceType("a", "b.a", [NamedType("a", "")]), + ) + assert NamedSequenceType("a", "b.a", [NamedType("a", "")]) != NamedSequenceType("a", "b.a", [NamedType("b", "")]) + assert hash(NamedSequenceType("a", "b.a", [NamedType("a", "")])) != hash( + NamedSequenceType("a", "b.a", [NamedType("b", "")]), + ) + + assert NamedSequenceType("a", "b.a", [NamedType("a", ""), LiteralType(["b"])]) == NamedSequenceType( + "a", + "b.a", + [LiteralType(["b"]), NamedType("a", "")], + ) + assert NamedSequenceType("a", "b.a", [NamedType("a", ""), LiteralType(["b"])]) != NamedSequenceType( + "a", + "b.a", + [LiteralType(["a"]), NamedType("b", "")], + ) + assert NamedSequenceType("a", "b.a", [NamedType("a", ""), NamedType("b", "")]) != NamedSequenceType( + "a", + "b.a", + [NamedType("a", ""), NamedType("c", "")], + ) + + def test_dict_type() -> None: dict_type = DictType( key_type=UnionType([NamedType("str", "builtins.str"), NamedType("int", "builtins.int")]), diff --git a/tests/safeds_stubgen/stubs_generator/__snapshots__/test_generate_stubs/test_stub_docstring_creation[numpydoc-NUMPYDOC].sdsstub b/tests/safeds_stubgen/stubs_generator/__snapshots__/test_generate_stubs/test_stub_docstring_creation[numpydoc-NUMPYDOC].sdsstub index 35943ec0..e502dd12 100644 --- a/tests/safeds_stubgen/stubs_generator/__snapshots__/test_generate_stubs/test_stub_docstring_creation[numpydoc-NUMPYDOC].sdsstub +++ b/tests/safeds_stubgen/stubs_generator/__snapshots__/test_generate_stubs/test_stub_docstring_creation[numpydoc-NUMPYDOC].sdsstub @@ -162,6 +162,19 @@ fun inferTypes3( b ) -> result1: union +/** + * numpy_sequence_types. + * + * Dolor sit amet. + * + * @result namedResult this will be the return value + */ +@Pure +@PythonName("numpy_sequence_types") +fun numpySequenceTypes( + a: SequenceTypeVar> +) -> namedResult: SequenceTypeVar2 + /** * ClassWithDocumentation. Code:: * From 954a1a49e5c187bde420f05ce973fb6824266f3d Mon Sep 17 00:00:00 2001 From: Arsam Date: Sat, 4 May 2024 12:25:18 +0200 Subject: [PATCH 3/3] Fixed a bug where stub Todo messages could not be created and would raise an exception --- src/safeds_stubgen/stubs_generator/_generate_stubs.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/safeds_stubgen/stubs_generator/_generate_stubs.py b/src/safeds_stubgen/stubs_generator/_generate_stubs.py index 889cd730..8f06bf89 100644 --- a/src/safeds_stubgen/stubs_generator/_generate_stubs.py +++ b/src/safeds_stubgen/stubs_generator/_generate_stubs.py @@ -749,7 +749,7 @@ def _create_type_string(self, type_data: dict | None) -> str: name = type_data["name"] if types: - if len(types) >= 2: + if len(types) >= 2 and name in {"Set", "List"}: self._current_todo_msgs.add(name) return f"{name}<{', '.join(types)}>" return f"{name}"