diff --git a/src/safeds_stubgen/_helpers.py b/src/safeds_stubgen/_helpers.py index 1ee8a684..35e144b8 100644 --- a/src/safeds_stubgen/_helpers.py +++ b/src/safeds_stubgen/_helpers.py @@ -38,7 +38,7 @@ def get_reexported_by(qname: str, reexport_map: dict[str, set[Module]]) -> list[ # Check if the module or the class/function itself are beeing reexported. If not, it means a # subpackage is beeing reexported. - if module.name == "__init__" and i < len(path)-1: + if module.name == "__init__" and i < len(path) - 1: zipped = list(zip(module.id.split("/"), path, strict=False)) # Check if there is a part of the paths that differs if not all(m == p for m, p in zipped): diff --git a/src/safeds_stubgen/api_analyzer/_ast_visitor.py b/src/safeds_stubgen/api_analyzer/_ast_visitor.py index 9c073426..6fd3567b 100644 --- a/src/safeds_stubgen/api_analyzer/_ast_visitor.py +++ b/src/safeds_stubgen/api_analyzer/_ast_visitor.py @@ -12,6 +12,7 @@ import safeds_stubgen.api_analyzer._types as sds_types from safeds_stubgen import is_internal +from safeds_stubgen._helpers import get_reexported_by from safeds_stubgen.api_analyzer._type_source_enums import TypeSourcePreference, TypeSourceWarning from safeds_stubgen.docstring_parsing import ResultDocstring @@ -42,7 +43,6 @@ mypy_expression_to_sds_type, mypy_variance_parser, ) -from safeds_stubgen._helpers import get_reexported_by if TYPE_CHECKING: from collections.abc import Generator @@ -299,7 +299,7 @@ def enter_funcdef(self, node: mp_nodes.FuncDef) -> None: and self.type_source_warning == TypeSourceWarning.WARN ): msg = f"Different type hint and docstring types for '{function_id}'." - logging.warning(msg) + logging.info(msg) if doc_type is not None and ( code_type is None or self.type_source_preference == TypeSourcePreference.DOCSTRING @@ -330,7 +330,7 @@ def enter_funcdef(self, node: mp_nodes.FuncDef) -> None: and self.type_source_warning == TypeSourceWarning.WARN ): msg = f"Different type hint and docstring types for the result of '{function_id}'." - logging.warning(msg) + logging.info(msg) if result_doc_type is not None: if result_type is None: @@ -425,8 +425,17 @@ def enter_assignmentstmt(self, node: mp_nodes.AssignmentStmt) -> None: continue if isinstance(parent, Class): - is_type_var = hasattr(node, "rvalue") and hasattr(node.rvalue, "analyzed") and isinstance(node.rvalue.analyzed, mp_nodes.TypeVarExpr) - for assignment in self._parse_attributes(lvalue, node.unanalyzed_type, is_static=True, is_type_var=is_type_var): + is_type_var = ( + hasattr(node, "rvalue") + and hasattr(node.rvalue, "analyzed") + and isinstance(node.rvalue.analyzed, mp_nodes.TypeVarExpr) + ) + for assignment in self._parse_attributes( + lvalue, + node.unanalyzed_type, + is_static=True, + is_type_var=is_type_var, + ): assignments.append(assignment) elif isinstance(parent, Function) and parent.name == "__init__": grand_parent = self.__declaration_stack[-2] @@ -835,7 +844,7 @@ def _parse_attributes( lvalue: mp_nodes.Expression, unanalyzed_type: mp_types.Type | None, is_static: bool = True, - is_type_var: bool = False + is_type_var: bool = False, ) -> list[Attribute]: """Parse the attributes from given Mypy expressions and return our own Attribute objects.""" assert isinstance(lvalue, mp_nodes.NameExpr | mp_nodes.MemberExpr | mp_nodes.TupleExpr) @@ -882,7 +891,7 @@ def _create_attribute( attribute: mp_nodes.Expression, unanalyzed_type: mp_types.Type | None, is_static: bool, - is_type_var: bool = False + is_type_var: bool = False, ) -> Attribute: """Create an Attribute object from a Mypy expression.""" # Get node information @@ -914,7 +923,9 @@ def _create_attribute( if not is_type_var and isinstance(attribute, mp_nodes.MemberExpr): if node is not None: attribute_type = node.type - if isinstance(attribute_type, mp_types.AnyType) and not has_correct_type_of_any(attribute_type.type_of_any): + if isinstance(attribute_type, mp_types.AnyType) and not has_correct_type_of_any( + attribute_type.type_of_any, + ): attribute_type = None else: # pragma: no cover # There seems to be a case where MemberExpr objects don't have node information (e.g. the @@ -924,7 +935,9 @@ def _create_attribute( # NameExpr are class attributes elif not is_type_var and isinstance(attribute, mp_nodes.NameExpr): - if node is not None and not node.explicit_self_type: + if node is not None and not hasattr(node, "explicit_self_type"): # pragma: no cover + pass + elif node is not None and not node.explicit_self_type: attribute_type = node.type # We need to get the unanalyzed_type for lists, since mypy is not able to check type hint information @@ -939,7 +952,7 @@ def _create_attribute( if unanalyzed_type is not None and hasattr(unanalyzed_type, "args"): attribute_type.args = unanalyzed_type.args else: # pragma: no cover - logging.warning("Could not get argument information for attribute.") + logging.info("Could not get argument information for attribute.") attribute_type = None type_ = sds_types.UnknownType() elif not unanalyzed_type: # pragma: no cover @@ -993,7 +1006,7 @@ def _parse_parameter_data(self, node: mp_nodes.FuncDef, function_id: str) -> lis # Get type information for parameter if mypy_type is None: msg = f"Could not parse the type for parameter {argument.variable.name} of function {node.fullname}." - logging.warning(msg) + logging.info(msg) arg_type = sds_types.UnknownType() elif isinstance(mypy_type, mp_types.AnyType) and not has_correct_type_of_any(mypy_type.type_of_any): # We try to infer the type through the default value later, if possible @@ -1008,7 +1021,7 @@ def _parse_parameter_data(self, node: mp_nodes.FuncDef, function_id: str) -> lis # way in Mypy. arg_type = self.mypy_type_to_abstract_type(type_annotation) elif type_annotation is not None: - arg_type = self.mypy_type_to_abstract_type(mypy_type) + arg_type = self.mypy_type_to_abstract_type(mypy_type, type_annotation) # Get default value and infer type information initializer = argument.initializer @@ -1031,6 +1044,10 @@ def _parse_parameter_data(self, node: mp_nodes.FuncDef, function_id: str) -> lis if isinstance(default_value, type): # pragma: no cover raise TypeError("default_value has the unexpected type 'type'.") + # Special case + if default_value == '"""': + default_value = '"\\""' + # Create parameter object arguments.append( Parameter( @@ -1064,7 +1081,7 @@ def _get_parameter_type_and_default_value( f"Could not parse parameter type for function {function_id}: Safe-DS does not support call " f"expressions as types." ) - logging.warning(msg) + logging.info(msg) # Safe-DS does not support call expressions as types return default_value, default_is_none elif isinstance(initializer, mp_nodes.UnaryExpr): @@ -1080,7 +1097,7 @@ def _get_parameter_type_and_default_value( f"Received the parameter {value} with an unexpected operator {initializer.op} for function " f"{function_id}. This parameter could not be parsed." ) - logging.warning(msg) + logging.info(msg) return UnknownValue(), default_is_none elif isinstance( initializer, @@ -1141,7 +1158,7 @@ def mypy_type_to_abstract_type( return sds_types.FinalType(type_=sds_types.LiteralType(literals=all_literals)) - logging.warning("Final type has no type arguments.") # pragma: no cover + logging.info("Final type has no type arguments.") # pragma: no cover return sds_types.FinalType(type_=sds_types.UnknownType()) # pragma: no cover return sds_types.FinalType(type_=sds_types.UnionType(types=types)) elif unanalyzed_type_name in {"list", "set"}: @@ -1162,18 +1179,23 @@ def mypy_type_to_abstract_type( if isinstance(mypy_type, mp_types.TupleType): return sds_types.TupleType(types=[self.mypy_type_to_abstract_type(item) for item in mypy_type.items]) elif isinstance(mypy_type, mp_types.UnionType): - if hasattr(unanalyzed_type, "items") and unanalyzed_type and len(getattr(unanalyzed_type, "items", [])) == len(mypy_type.items): + unanalyzed_type_items = getattr(unanalyzed_type, "items", []) + if ( + hasattr(unanalyzed_type, "items") + and unanalyzed_type + and len(unanalyzed_type_items) == len(mypy_type.items) + ): return sds_types.UnionType( types=[ - self.mypy_type_to_abstract_type(mypy_type.items[i], unanalyzed_type.items[i]) + self.mypy_type_to_abstract_type(mypy_type.items[i], unanalyzed_type_items[i]) for i in range(len(mypy_type.items)) - ] + ], ) return sds_types.UnionType(types=[self.mypy_type_to_abstract_type(item) for item in mypy_type.items]) # Special Cases elif isinstance(mypy_type, mp_types.TypeAliasType): - fullname = mypy_type.alias.fullname + fullname = getattr(mypy_type.alias, "fullname", "") name = getattr(mypy_type.alias, "name", fullname.split(".")[-1]) return sds_types.NamedType(name=name, qname=fullname) elif isinstance(mypy_type, mp_types.TypeVarType): @@ -1199,18 +1221,23 @@ def mypy_type_to_abstract_type( # If the Any type is generated b/c of from_unimported_type, then we can parse the type # from the import information if mypy_type.missing_import_name is None: # pragma: no cover - logging.warning("Could not parse a type, added unknown type instead.") + logging.info("Could not parse a type, added unknown type instead.") return sds_types.UnknownType() missing_import_name = mypy_type.missing_import_name.split(".")[-1] # type: ignore[union-attr] name, qname = self._find_alias(missing_import_name) - if unanalyzed_type and hasattr(unanalyzed_type, "name") and "." in unanalyzed_type.name and unanalyzed_type.name.startswith(missing_import_name): + if ( + unanalyzed_type + and hasattr(unanalyzed_type, "name") + and "." in unanalyzed_type.name + and unanalyzed_type.name.startswith(missing_import_name) + ): name = unanalyzed_type.name.split(".")[-1] qname = unanalyzed_type.name.replace(missing_import_name, qname) if not qname: # pragma: no cover - logging.warning("Could not parse a type, added unknown type instead.") + logging.info("Could not parse a type, added unknown type instead.") return sds_types.UnknownType() return sds_types.NamedType(name=name, qname=qname) @@ -1222,13 +1249,11 @@ def mypy_type_to_abstract_type( return sds_types.LiteralType(literals=[mypy_type.value]) elif isinstance(mypy_type, mp_types.UnboundType): if mypy_type.name in {"list", "set", "tuple"}: - return { + return { # type: ignore[abstract] "list": sds_types.ListType, "set": sds_types.SetType, "tuple": sds_types.TupleType, - }[ - mypy_type.name - ](types=[self.mypy_type_to_abstract_type(arg) for arg in mypy_type.args]) + }[mypy_type.name](types=[self.mypy_type_to_abstract_type(arg) for arg in mypy_type.args]) # Get qname if mypy_type.name in {"Any", "str", "int", "bool", "float", "None"}: @@ -1249,7 +1274,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.") + logging.info("Could not parse a type, added unknown type instead.") return sds_types.UnknownType() return sds_types.NamedType(name=name, qname=qname) @@ -1286,7 +1311,7 @@ def mypy_type_to_abstract_type( 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 + logging.info("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/_get_api.py b/src/safeds_stubgen/api_analyzer/_get_api.py index 28522eba..6b3fd50c 100644 --- a/src/safeds_stubgen/api_analyzer/_get_api.py +++ b/src/safeds_stubgen/api_analyzer/_get_api.py @@ -217,7 +217,7 @@ def _get_aliases(result_types: dict, package_name: str) -> dict[str, set[str]]: fullname = key.node.fullname else: # pragma: no cover msg = f"Received unexpected type while searching for aliases. Skipping for '{name}'." - logging.warning(msg) + logging.info(msg) continue aliases[name].add(fullname) diff --git a/src/safeds_stubgen/api_analyzer/_mypy_helpers.py b/src/safeds_stubgen/api_analyzer/_mypy_helpers.py index 865b3f27..675376dd 100644 --- a/src/safeds_stubgen/api_analyzer/_mypy_helpers.py +++ b/src/safeds_stubgen/api_analyzer/_mypy_helpers.py @@ -118,11 +118,12 @@ def mypy_expression_to_sds_type(expr: mp_nodes.Expression) -> sds_types.Abstract if expr.op == "not": return sds_types.NamedType(name="bool", qname="builtins.bool") return mypy_expression_to_sds_type(expr.expr) - elif ((isinstance(expr, mp_nodes.OpExpr) and expr.op in {"or", "and"}) or - (isinstance(expr, mp_nodes.ComparisonExpr) and ("is not" in expr.operators or "is" in expr.operators))): + elif (isinstance(expr, mp_nodes.OpExpr) and expr.op in {"or", "and"}) or ( + isinstance(expr, mp_nodes.ComparisonExpr) and ("is not" in expr.operators or "is" in expr.operators) + ): return sds_types.NamedType(name="bool", qname="builtins.bool") - logging.warning( + logging.info( "Could not parse a parameter or return type for a function: Safe-DS does not support " "types such as call expressions. Added 'unknown' instead.", ) diff --git a/tests/data/various_modules_package/__init__.py b/tests/data/various_modules_package/__init__.py index 25bd9a4e..9503dce6 100644 --- a/tests/data/various_modules_package/__init__.py +++ b/tests/data/various_modules_package/__init__.py @@ -11,6 +11,7 @@ from file_creation._module_3 import Reexported from .class_module import ClassModuleClassD as ClMCD from file_creation.module_1 import Lv2 +from tests.data.various_modules_package import reexport_test __all__ = [ "reex_1", diff --git a/tests/data/various_modules_package/another_path/__init__.py b/tests/data/various_modules_package/another_path/__init__.py index 9811f6ff..76089c80 100644 --- a/tests/data/various_modules_package/another_path/__init__.py +++ b/tests/data/various_modules_package/another_path/__init__.py @@ -1,5 +1,10 @@ from typing import TYPE_CHECKING +# Testing if the "not_reexported" module is beeing wrongly reexported b/c of the existence +# of file_creation/package_1/not_reexported.py +from not_reexported import * +import not_reexported + if TYPE_CHECKING: from another_module import _YetAnotherPrivateClass diff --git a/tests/data/various_modules_package/another_path/not_reexported.py b/tests/data/various_modules_package/another_path/not_reexported.py new file mode 100644 index 00000000..e69de29b diff --git a/tests/data/various_modules_package/file_creation/package_1/not_reexported.py b/tests/data/various_modules_package/file_creation/package_1/not_reexported.py new file mode 100644 index 00000000..04e82710 --- /dev/null +++ b/tests/data/various_modules_package/file_creation/package_1/not_reexported.py @@ -0,0 +1,2 @@ +class NotReexported(): + ... diff --git a/tests/data/various_modules_package/function_module.py b/tests/data/various_modules_package/function_module.py index c99d4b0f..4c61de77 100644 --- a/tests/data/various_modules_package/function_module.py +++ b/tests/data/various_modules_package/function_module.py @@ -87,6 +87,7 @@ def params_with_default_value( tuple_: tuple[int, str, bool] = (1, "2", True), literal: Literal["Some String"] = "Some String", any_: Any = False, + single_quote: str = '"' ): ... @@ -274,6 +275,10 @@ def alias_subclass_result_type() -> ArrayLike | np.ndarray: ... +def alias_subclass_param_type(x: ArrayLike | np.ndarray): + ... + + def different_result_operants(y): if y: return False diff --git a/tests/data/various_modules_package/reexport_test/__init__.py b/tests/data/various_modules_package/reexport_test/__init__.py new file mode 100644 index 00000000..e69de29b diff --git a/tests/data/various_modules_package/reexport_test/reexport_test_2/__init__.py b/tests/data/various_modules_package/reexport_test/reexport_test_2/__init__.py new file mode 100644 index 00000000..e69de29b diff --git a/tests/data/various_modules_package/reexport_test/reexport_test_2/reexport_test_3/__init__.py b/tests/data/various_modules_package/reexport_test/reexport_test_2/reexport_test_3/__init__.py new file mode 100644 index 00000000..e69de29b diff --git a/tests/data/various_modules_package/reexport_test/reexport_test_2/reexport_test_3/reexport_test.py b/tests/data/various_modules_package/reexport_test/reexport_test_2/reexport_test_3/reexport_test.py new file mode 100644 index 00000000..2e2c0eb1 --- /dev/null +++ b/tests/data/various_modules_package/reexport_test/reexport_test_2/reexport_test_3/reexport_test.py @@ -0,0 +1,4 @@ +# With this directory we test the reexportation of this file if the root directory imports reexport_test. + +class NotReexportedTest: + ... diff --git a/tests/safeds_stubgen/api_analyzer/__snapshots__/test__get_api.ambr b/tests/safeds_stubgen/api_analyzer/__snapshots__/test__get_api.ambr index 109ee28b..9b81e2a7 100644 --- a/tests/safeds_stubgen/api_analyzer/__snapshots__/test__get_api.ambr +++ b/tests/safeds_stubgen/api_analyzer/__snapshots__/test__get_api.ambr @@ -5491,10 +5491,36 @@ }), ]) # --- -# name: test_function_results[none_result] +# name: test_function_results[none_result_1] list([ dict({ - 'id': 'tests/data/various_modules_package/function_module/none_result/result_1', + 'id': 'tests/data/various_modules_package/function_module/none_result_1/result_1', + 'name': 'result_1', + 'type': dict({ + 'kind': 'NamedType', + 'name': 'None', + 'qname': 'builtins.None', + }), + }), + ]) +# --- +# name: test_function_results[none_result_2] + list([ + dict({ + 'id': 'tests/data/various_modules_package/function_module/none_result_2/result_1', + 'name': 'result_1', + 'type': dict({ + 'kind': 'NamedType', + 'name': 'None', + 'qname': 'builtins.None', + }), + }), + ]) +# --- +# name: test_function_results[none_result_3] + list([ + dict({ + 'id': 'tests/data/various_modules_package/function_module/none_result_3/result_1', 'name': 'result_1', 'type': dict({ 'kind': 'NamedType', @@ -5949,6 +5975,27 @@ 'results': list([ ]), }), + dict({ + 'docstring': dict({ + 'description': '', + 'examples': list([ + ]), + 'full_docstring': '', + }), + 'id': 'tests/data/various_modules_package/function_module/alias_subclass_param_type', + 'is_class_method': False, + 'is_property': False, + 'is_public': True, + 'is_static': False, + 'name': 'alias_subclass_param_type', + 'parameters': list([ + 'tests/data/various_modules_package/function_module/alias_subclass_param_type/x', + ]), + 'reexported_by': list([ + ]), + 'results': list([ + ]), + }), dict({ 'docstring': dict({ 'description': '', @@ -6458,18 +6505,60 @@ ]), 'full_docstring': '', }), - 'id': 'tests/data/various_modules_package/function_module/none_result', + 'id': 'tests/data/various_modules_package/function_module/none_result_1', + 'is_class_method': False, + 'is_property': False, + 'is_public': True, + 'is_static': False, + 'name': 'none_result_1', + 'parameters': list([ + ]), + 'reexported_by': list([ + ]), + 'results': list([ + 'tests/data/various_modules_package/function_module/none_result_1/result_1', + ]), + }), + dict({ + 'docstring': dict({ + 'description': '', + 'examples': list([ + ]), + 'full_docstring': '', + }), + 'id': 'tests/data/various_modules_package/function_module/none_result_2', + 'is_class_method': False, + 'is_property': False, + 'is_public': True, + 'is_static': False, + 'name': 'none_result_2', + 'parameters': list([ + ]), + 'reexported_by': list([ + ]), + 'results': list([ + 'tests/data/various_modules_package/function_module/none_result_2/result_1', + ]), + }), + dict({ + 'docstring': dict({ + 'description': '', + 'examples': list([ + ]), + 'full_docstring': '', + }), + 'id': 'tests/data/various_modules_package/function_module/none_result_3', 'is_class_method': False, 'is_property': False, 'is_public': True, 'is_static': False, - 'name': 'none_result', + 'name': 'none_result_3', 'parameters': list([ ]), 'reexported_by': list([ ]), 'results': list([ - 'tests/data/various_modules_package/function_module/none_result/result_1', + 'tests/data/various_modules_package/function_module/none_result_3/result_1', ]), }), dict({ @@ -6662,6 +6751,7 @@ 'tests/data/various_modules_package/function_module/params_with_default_value/tuple_', 'tests/data/various_modules_package/function_module/params_with_default_value/literal', 'tests/data/various_modules_package/function_module/params_with_default_value/any_', + 'tests/data/various_modules_package/function_module/params_with_default_value/single_quote', ]), 'reexported_by': list([ ]), @@ -6888,27 +6978,6 @@ 'tests/data/various_modules_package/function_module/return_param4/result_4', ]), }), - dict({ - 'docstring': dict({ - 'description': '', - 'examples': list([ - ]), - 'full_docstring': '', - }), - 'id': 'tests/data/various_modules_package/function_module/return_without_result', - 'is_class_method': False, - 'is_property': False, - 'is_public': True, - 'is_static': False, - 'name': 'return_without_result', - 'parameters': list([ - ]), - 'reexported_by': list([ - ]), - 'results': list([ - 'tests/data/various_modules_package/function_module/return_without_result/result_1', - ]), - }), dict({ 'docstring': dict({ 'description': '', @@ -7119,6 +7188,10 @@ 'alias': None, 'qualified_name': 'file_creation.module_1.Lv2', }), + dict({ + 'alias': None, + 'qualified_name': 'tests.data.various_modules_package.reexport_test', + }), ]) # --- # name: test_imports[__init__ (wildcard_imports)] @@ -7240,6 +7313,10 @@ 'alias': None, 'qualified_name': 'file_creation.module_1.Lv2', }), + dict({ + 'alias': None, + 'qualified_name': 'tests.data.various_modules_package.reexport_test', + }), ]), 'wildcard_imports': list([ dict({ @@ -7480,12 +7557,19 @@ 'alias': None, 'qualified_name': 'typing.TYPE_CHECKING', }), + dict({ + 'alias': None, + 'qualified_name': 'not_reexported', + }), dict({ 'alias': None, 'qualified_name': 'another_module._YetAnotherPrivateClass', }), ]), 'wildcard_imports': list([ + dict({ + 'module_name': 'not_reexported', + }), ]), }) # --- @@ -7582,6 +7666,23 @@ ]), }) # --- +# name: test_modules[another_path/not_reexported] + dict({ + 'classes': list([ + ]), + 'docstring': '', + 'enums': list([ + ]), + 'functions': list([ + ]), + 'id': 'tests/data/various_modules_package/another_path/not_reexported', + 'name': 'not_reexported', + 'qualified_imports': list([ + ]), + 'wildcard_imports': list([ + ]), + }) +# --- # name: test_modules[attribute_module] dict({ 'classes': list([ @@ -7917,6 +8018,24 @@ ]), }) # --- +# name: test_modules[file_creation/package_1/not_reexported] + dict({ + 'classes': list([ + 'tests/data/various_modules_package/file_creation/package_1/not_reexported/NotReexported', + ]), + 'docstring': '', + 'enums': list([ + ]), + 'functions': list([ + ]), + 'id': 'tests/data/various_modules_package/file_creation/package_1/not_reexported', + 'name': 'not_reexported', + 'qualified_imports': list([ + ]), + 'wildcard_imports': list([ + ]), + }) +# --- # name: test_modules[function_module] dict({ 'classes': list([ @@ -7943,7 +8062,6 @@ 'tests/data/various_modules_package/function_module/str_result', 'tests/data/various_modules_package/function_module/bool_result', 'tests/data/various_modules_package/function_module/float_result', - 'tests/data/various_modules_package/function_module/none_result', 'tests/data/various_modules_package/function_module/obj_result', 'tests/data/various_modules_package/function_module/callexr_result_class', 'tests/data/various_modules_package/function_module/callexr_result_function', @@ -7974,10 +8092,13 @@ 'tests/data/various_modules_package/function_module/return_param3', 'tests/data/various_modules_package/function_module/return_param4', 'tests/data/various_modules_package/function_module/return_not_statement', - 'tests/data/various_modules_package/function_module/return_without_result', 'tests/data/various_modules_package/function_module/type_alias_param', 'tests/data/various_modules_package/function_module/alias_subclass_result_type', + 'tests/data/various_modules_package/function_module/alias_subclass_param_type', 'tests/data/various_modules_package/function_module/different_result_operants', + 'tests/data/various_modules_package/function_module/none_result_1', + 'tests/data/various_modules_package/function_module/none_result_2', + 'tests/data/various_modules_package/function_module/none_result_3', ]), 'id': 'tests/data/various_modules_package/function_module', 'name': 'function_module', @@ -8099,6 +8220,75 @@ ]), }) # --- +# name: test_modules[reexport_test/__init__] + dict({ + 'classes': list([ + ]), + 'docstring': '', + 'enums': list([ + ]), + 'functions': list([ + ]), + 'id': 'tests/data/various_modules_package/reexport_test', + 'name': '__init__', + 'qualified_imports': list([ + ]), + 'wildcard_imports': list([ + ]), + }) +# --- +# name: test_modules[reexport_test/reexport_test_2/__init__] + dict({ + 'classes': list([ + ]), + 'docstring': '', + 'enums': list([ + ]), + 'functions': list([ + ]), + 'id': 'tests/data/various_modules_package/reexport_test/reexport_test_2', + 'name': '__init__', + 'qualified_imports': list([ + ]), + 'wildcard_imports': list([ + ]), + }) +# --- +# name: test_modules[reexport_test/reexport_test_2/reexport_test_3/__init__] + dict({ + 'classes': list([ + ]), + 'docstring': '', + 'enums': list([ + ]), + 'functions': list([ + ]), + 'id': 'tests/data/various_modules_package/reexport_test/reexport_test_2/reexport_test_3', + 'name': '__init__', + 'qualified_imports': list([ + ]), + 'wildcard_imports': list([ + ]), + }) +# --- +# name: test_modules[reexport_test/reexport_test_2/reexport_test_3/reexport_test] + dict({ + 'classes': list([ + 'tests/data/various_modules_package/reexport_test/reexport_test_2/reexport_test_3/reexport_test/NotReexportedTest', + ]), + 'docstring': '', + 'enums': list([ + ]), + 'functions': list([ + ]), + 'id': 'tests/data/various_modules_package/reexport_test/reexport_test_2/reexport_test_3/reexport_test', + 'name': 'reexport_test', + 'qualified_imports': list([ + ]), + 'wildcard_imports': list([ + ]), + }) +# --- # name: test_modules[type_var_module] dict({ 'classes': list([ diff --git a/tests/safeds_stubgen/api_analyzer/test__get_api.py b/tests/safeds_stubgen/api_analyzer/test__get_api.py index 25f4edfd..c8f6285a 100644 --- a/tests/safeds_stubgen/api_analyzer/test__get_api.py +++ b/tests/safeds_stubgen/api_analyzer/test__get_api.py @@ -470,7 +470,9 @@ def test_function_parameters( ("int_result", _function_module_name, "", "plaintext"), ("str_result", _function_module_name, "", "plaintext"), ("float_result", _function_module_name, "", "plaintext"), - ("none_result", _function_module_name, "", "plaintext"), + ("none_result_1", _function_module_name, "", "plaintext"), + ("none_result_2", _function_module_name, "", "plaintext"), + ("none_result_3", _function_module_name, "", "plaintext"), ("obj_result", _function_module_name, "", "plaintext"), ("callexr_result_class", _function_module_name, "", "plaintext"), ("callexr_result_function", _function_module_name, "", "plaintext"), @@ -510,7 +512,9 @@ def test_function_parameters( "int_result", "str_result", "float_result", - "none_result", + "none_result_1", + "none_result_2", + "none_result_3", "obj_result", "callexr_result_class", "callexr_result_function", diff --git a/tests/safeds_stubgen/stubs_generator/__snapshots__/test_generate_stubs/TestStubFileGeneration.test_stub_creation[function_module].sdsstub b/tests/safeds_stubgen/stubs_generator/__snapshots__/test_generate_stubs/TestStubFileGeneration.test_stub_creation[function_module].sdsstub index ec930c2e..319c2b00 100644 --- a/tests/safeds_stubgen/stubs_generator/__snapshots__/test_generate_stubs/TestStubFileGeneration.test_stub_creation[function_module].sdsstub +++ b/tests/safeds_stubgen/stubs_generator/__snapshots__/test_generate_stubs/TestStubFileGeneration.test_stub_creation[function_module].sdsstub @@ -67,7 +67,8 @@ fun paramsWithDefaultValue( optional: Int? = null, @PythonName("tuple_") tuple: Tuple, `literal`: literal<"Some String"> = "Some String", - @PythonName("any_") any: Any = false + @PythonName("any_") any: Any = false, + @PythonName("single_quote") singleQuote: String = "\"" ) // TODO List type has to many type arguments. @@ -347,6 +348,13 @@ fun typeAliasParam( @PythonName("alias_subclass_result_type") fun aliasSubclassResultType() -> result1: union +// TODO Result type information missing. +@Pure +@PythonName("alias_subclass_param_type") +fun aliasSubclassParamType( + x: union +) + // TODO Some parameter have no type information. @Pure @PythonName("different_result_operants") diff --git a/tests/safeds_stubgen/stubs_generator/__snapshots__/test_generate_stubs/TestStubFileGeneration.test_stub_creation[not_reexported].sdsstub b/tests/safeds_stubgen/stubs_generator/__snapshots__/test_generate_stubs/TestStubFileGeneration.test_stub_creation[not_reexported].sdsstub new file mode 100644 index 00000000..ba0b3870 --- /dev/null +++ b/tests/safeds_stubgen/stubs_generator/__snapshots__/test_generate_stubs/TestStubFileGeneration.test_stub_creation[not_reexported].sdsstub @@ -0,0 +1,4 @@ +@PythonModule("tests.data.various_modules_package.file_creation.package_1.not_reexported") +package tests.data.variousModulesPackage.fileCreation.package1.notReexported + +class NotReexported() diff --git a/tests/safeds_stubgen/stubs_generator/__snapshots__/test_generate_stubs/TestStubFileGeneration.test_stub_creation[reexport_test].sdsstub b/tests/safeds_stubgen/stubs_generator/__snapshots__/test_generate_stubs/TestStubFileGeneration.test_stub_creation[reexport_test].sdsstub new file mode 100644 index 00000000..c9889e99 --- /dev/null +++ b/tests/safeds_stubgen/stubs_generator/__snapshots__/test_generate_stubs/TestStubFileGeneration.test_stub_creation[reexport_test].sdsstub @@ -0,0 +1,4 @@ +@PythonModule("tests.data.various_modules_package.reexport_test.reexport_test_2.reexport_test_3.reexport_test") +package tests.data.variousModulesPackage.reexportTest.reexportTest2.reexportTest3.reexportTest + +class NotReexportedTest() diff --git a/tests/safeds_stubgen/stubs_generator/test_generate_stubs.py b/tests/safeds_stubgen/stubs_generator/test_generate_stubs.py index 9f68979b..a32278bc 100644 --- a/tests/safeds_stubgen/stubs_generator/test_generate_stubs.py +++ b/tests/safeds_stubgen/stubs_generator/test_generate_stubs.py @@ -50,6 +50,7 @@ def test_file_creation() -> None: ), ("tests/data/various_modules_package/file_creation/module_1", "module_1"), ("tests/data/various_modules_package/file_creation/package_1/module_5", "module_5"), + ("tests/data/various_modules_package/file_creation/package_1/not_reexported", "not_reexported"), ("tests/data/various_modules_package/file_creation/public_reexported", "public_reexported"), ("tests/data/various_modules_package/file_creation", "reexported_from_another_package_3"), (