From 7366e042bc50c6529094942b3e94a16711343c97 Mon Sep 17 00:00:00 2001 From: Arsam Date: Sun, 10 Dec 2023 20:16:14 +0100 Subject: [PATCH 01/10] Added TypeVarType class and added test cases for it; fixed the bug where some packages couldn't be analyzed b/c the mypy build result could not be parsed from our side; some bug fixes and refactoring --- .../api_analyzer/_ast_visitor.py | 52 ++++--- src/safeds_stubgen/api_analyzer/_get_api.py | 49 +++--- .../api_analyzer/_mypy_helpers.py | 2 + src/safeds_stubgen/api_analyzer/_types.py | 15 ++ .../stubs_generator/_generate_stubs.py | 3 + .../attribute_module.py | 4 +- .../function_module.py | 6 +- .../infer_types_module.py | 8 + .../__snapshots__/test__get_api.ambr | 142 ++++++++++++++++++ .../api_analyzer/test__get_api.py | 4 + .../api_analyzer/test_api_visitor.py | 22 +-- .../__snapshots__/test_generate_stubs.ambr | 20 +++ 12 files changed, 261 insertions(+), 66 deletions(-) diff --git a/src/safeds_stubgen/api_analyzer/_ast_visitor.py b/src/safeds_stubgen/api_analyzer/_ast_visitor.py index ba308cd6..fac88cfc 100644 --- a/src/safeds_stubgen/api_analyzer/_ast_visitor.py +++ b/src/safeds_stubgen/api_analyzer/_ast_visitor.py @@ -88,13 +88,12 @@ def enter_moduledef(self, node: mp_nodes.MypyFile) -> None: docstring = definition.expr.value # Create module id to get the full path - id_ = self._create_module_id(node.fullname) + id_ = self._create_module_id(node.path) # If we are checking a package node.name will be the package name, but since we get import information from # the __init__.py file we set the name to __init__ if is_package: name = "__init__" - id_ += f"/{name}" else: name = node.name @@ -423,7 +422,7 @@ def _infer_type_from_return_stmts(func_node: mp_nodes.FuncDef) -> sds_types.Name # make a typecheck anyway for the mypy linter. types = set() for return_stmt in return_stmts: - if return_stmt.expr is not None: + if return_stmt.expr is not None and not isinstance(return_stmt.expr, mp_nodes.CallExpr): type_ = mypy_expression_to_sds_type(return_stmt.expr) if isinstance(type_, sds_types.NamedType | sds_types.TupleType): types.add(type_) @@ -434,9 +433,10 @@ def _infer_type_from_return_stmts(func_node: mp_nodes.FuncDef) -> sds_types.Name key=lambda x: (x.name if isinstance(x, sds_types.NamedType) else str(len(x.types))), ) - if len(return_stmt_types) >= 2: + if len(return_stmt_types) == 1: + return return_stmt_types[0] + elif len(return_stmt_types) >= 2: return sds_types.TupleType(types=return_stmt_types) - return return_stmt_types[0] return None @staticmethod @@ -561,11 +561,19 @@ def _create_attribute( qname = getattr(attribute, "fullname", "") # Get node information + type_ = None + node = None if hasattr(attribute, "node"): - if not isinstance(attribute.node, mp_nodes.Var): # pragma: no cover - raise TypeError("node has wrong type") + if not isinstance(attribute.node, mp_nodes.Var): + # In this case we have a TypeVar attribute + attr_name = getattr(attribute, "name", "") + + if not attr_name: # pragma: no Cover + raise AttributeError("Expected TypeVar to have attribute 'name'.") - node: mp_nodes.Var = attribute.node + type_ = sds_types.TypeVarType(attr_name) + else: + node = attribute.node else: # pragma: no cover raise AttributeError("Expected attribute to have attribute 'node'.") @@ -576,13 +584,13 @@ def _create_attribute( attribute_type = None # MemberExpr are constructor (__init__) attributes - if isinstance(attribute, mp_nodes.MemberExpr): + if node is not None and isinstance(attribute, mp_nodes.MemberExpr): attribute_type = node.type if isinstance(attribute_type, mp_types.AnyType) and not has_correct_type_of_any(attribute_type.type_of_any): attribute_type = None # NameExpr are class attributes - elif isinstance(attribute, mp_nodes.NameExpr): + elif node is not None and isinstance(attribute, mp_nodes.NameExpr): if not node.explicit_self_type: attribute_type = node.type @@ -600,10 +608,6 @@ def _create_attribute( else: # pragma: no cover raise AttributeError("Could not get argument information for attribute.") - else: # pragma: no cover - raise TypeError("Attribute has an unexpected type.") - - type_ = None # Ignore types that are special mypy any types if attribute_type is not None and not ( isinstance(attribute_type, mp_types.AnyType) and not has_correct_type_of_any(attribute_type.type_of_any) @@ -770,7 +774,7 @@ def _add_reexports(self, module: Module) -> None: def _check_if_qname_in_package(self, qname: str) -> bool: return self.api.package in qname - def _create_module_id(self, qname: str) -> str: + def _create_module_id(self, module_path: str) -> str: """Create an ID for the module object. Creates the module ID while discarding possible unnecessary information from the module qname. @@ -787,19 +791,23 @@ def _create_module_id(self, qname: str) -> str: """ package_name = self.api.package - if package_name not in qname: - raise ValueError("Package name could not be found in the qualified name of the module.") + if package_name not in module_path: + raise ValueError("Package name could not be found in the module path.") # We have to split the qname of the module at the first occurence of the package name and reconnect it while # discarding everything in front of it. This is necessary since the qname could contain unwanted information. - module_id = qname.split(f"{package_name}", 1)[-1] + module_id = module_path.split(package_name, 1)[-1] + module_id = module_id.replace("\\", '/') - if module_id.startswith("."): + if module_id.startswith("/"): module_id = module_id[1:] - # Replaces dots with slashes and add the package name at the start of the id, since we removed it - module_id = f"/{module_id.replace('.', '/')}" if module_id else "" - return f"{package_name}{module_id}" + if module_id.endswith('.py'): + module_id = module_id[:-3] + + if module_id: + return f"{package_name}/{module_id}" + return package_name def _is_public(self, name: str, qualified_name: str) -> bool: if name.startswith("_") and not name.endswith("__"): diff --git a/src/safeds_stubgen/api_analyzer/_get_api.py b/src/safeds_stubgen/api_analyzer/_get_api.py index e20b1c6f..a9eb628c 100644 --- a/src/safeds_stubgen/api_analyzer/_get_api.py +++ b/src/safeds_stubgen/api_analyzer/_get_api.py @@ -79,37 +79,24 @@ def _get_mypy_ast(files: list[str], package_paths: list[Path], root: Path) -> li result = mypy_build.build(mypyfiles, options=opt) # Check mypy data key root start - parts = root.parts - graph_keys = list(result.graph.keys()) - root_start_after = -1 - for i in range(len(parts)): - if ".".join(parts[i:]) in graph_keys: - root_start_after = i - break - - # Create the keys for getting the corresponding data - packages = [ - ".".join( - package_path.parts[root_start_after:], - ).replace(".py", "") - for package_path in package_paths - ] - - modules = [ - ".".join( - Path(file).parts[root_start_after:], - ).replace(".py", "") - for file in files - ] - - # Get the needed data from mypy. The packages need to be checked first, since we have - # to get the reexported data first - all_paths = packages + modules + graphs = result.graph + graph_keys = list(graphs.keys()) + root_path = str(root) + # Get the needed data from mypy. The __init__ files need to be checked first, since we have to get the + # reexported data for the packages first results = [] - for path_key in all_paths: - tree = result.graph[path_key].tree - if tree is not None: - results.append(tree) + init_results = [] + for graph_key in graph_keys: + graph = graphs[graph_key] + abs_path = graph.abspath - return results + if root_path not in abs_path or not abs_path.endswith(".py"): + continue + + if abs_path.endswith("__init__.py"): + init_results.append(graph.tree) + else: + results.append(graph.tree) + + return init_results + results diff --git a/src/safeds_stubgen/api_analyzer/_mypy_helpers.py b/src/safeds_stubgen/api_analyzer/_mypy_helpers.py index 99e3b4ba..49b6066f 100644 --- a/src/safeds_stubgen/api_analyzer/_mypy_helpers.py +++ b/src/safeds_stubgen/api_analyzer/_mypy_helpers.py @@ -66,6 +66,8 @@ def mypy_type_to_abstract_type( return sds_types.UnionType(types=[mypy_type_to_abstract_type(item) for item in mypy_type.items]) # Special Cases + elif isinstance(mypy_type, mp_types.TypeVarType): + return sds_types.TypeVarType(mypy_type.name) elif isinstance(mypy_type, mp_types.CallableType): return sds_types.CallableType( parameter_types=[mypy_type_to_abstract_type(arg_type) for arg_type in mypy_type.arg_types], diff --git a/src/safeds_stubgen/api_analyzer/_types.py b/src/safeds_stubgen/api_analyzer/_types.py index cc908413..fc63910e 100644 --- a/src/safeds_stubgen/api_analyzer/_types.py +++ b/src/safeds_stubgen/api_analyzer/_types.py @@ -387,6 +387,21 @@ def __hash__(self) -> int: return hash(frozenset(self.types)) +# Todo Frage: Wie stellen wir TypeVar dar? +@dataclass(frozen=True) +class TypeVarType(AbstractType): + name: str + + @classmethod + def from_dict(cls, d: dict[str, str]) -> TypeVarType: + return TypeVarType(d["name"]) + + def to_dict(self) -> dict[str, str]: + return {"kind": self.__class__.__name__, "name": self.name} + + def __hash__(self) -> int: + return hash(frozenset([self.name])) + # ############################## Utilities ############################## # # def _dismantel_type_string_structure(type_structure: str) -> list: # current_type = "" diff --git a/src/safeds_stubgen/stubs_generator/_generate_stubs.py b/src/safeds_stubgen/stubs_generator/_generate_stubs.py index cbf9e159..e9b50294 100644 --- a/src/safeds_stubgen/stubs_generator/_generate_stubs.py +++ b/src/safeds_stubgen/stubs_generator/_generate_stubs.py @@ -621,6 +621,9 @@ def _create_type_string(self, type_data: dict | None) -> str: else: types.append(f"{literal_type}") return f"literal<{', '.join(types)}>" + # Todo Frage: Wie stellen wir TypeVar dar? + elif kind == "TypeVarType": + return f"{type_data['name']}" raise ValueError(f"Unexpected type: {kind}") # pragma: no cover diff --git a/tests/data/various_modules_package/attribute_module.py b/tests/data/various_modules_package/attribute_module.py index a2bd7c34..8389c5b3 100644 --- a/tests/data/various_modules_package/attribute_module.py +++ b/tests/data/various_modules_package/attribute_module.py @@ -1,4 +1,4 @@ -from typing import Optional, Final, Literal +from typing import Optional, Final, Literal, TypeVar class AttributesClassA: @@ -62,5 +62,7 @@ def some_func() -> bool: multi_attr_5, multi_attr_6 = ("A", "B") multi_attr_7 = multi_attr_8 = "A" + type_var = TypeVar("type_var") + def __init__(self): self.init_attr: bool = False diff --git a/tests/data/various_modules_package/function_module.py b/tests/data/various_modules_package/function_module.py index 0a1df3c7..7f114d0d 100644 --- a/tests/data/various_modules_package/function_module.py +++ b/tests/data/various_modules_package/function_module.py @@ -1,4 +1,4 @@ -from typing import Callable, Optional, Literal, Any +from typing import Callable, Optional, Literal, Any, TypeVar class FunctionModuleClassA: @@ -179,6 +179,10 @@ def any_results() -> Any: ... def callable_type(param: Callable[[str], tuple[int, str]]) -> Callable[[int, int], int]: ... +_type_var = TypeVar("_type_var") +def type_var_func(type_var_list: list[_type_var]) -> list[_type_var]: ... + + class FunctionModulePropertiesClass: @property def property_function(self): ... diff --git a/tests/data/various_modules_package/infer_types_module.py b/tests/data/various_modules_package/infer_types_module.py index c6a34d96..3256b6e2 100644 --- a/tests/data/various_modules_package/infer_types_module.py +++ b/tests/data/various_modules_package/infer_types_module.py @@ -82,3 +82,11 @@ def infer_function_2(i=2): return "a" else: return False + + def infer_call_result_1(self): + return self.infer_function_2() + + def _(self) -> str: ... + + def infer_call_result_2(self): + return self._() 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 79aefcae..cb38566b 100644 --- a/tests/safeds_stubgen/api_analyzer/__snapshots__/test__get_api.ambr +++ b/tests/safeds_stubgen/api_analyzer/__snapshots__/test__get_api.ambr @@ -984,6 +984,21 @@ 'qname': 'builtins.int', }), }), + dict({ + 'docstring': dict({ + 'default_value': '', + 'description': '', + 'type': '', + }), + 'id': 'various_modules_package/attribute_module/AttributesClassB/type_var', + 'is_public': True, + 'is_static': True, + 'name': 'type_var', + 'type': dict({ + 'kind': 'TypeVarType', + 'name': 'type_var', + }), + }), ]) # --- # name: test_class_attributes[ClassModuleNestedClassE] @@ -1567,6 +1582,64 @@ # --- # name: test_class_methods[InferMyTypes] list([ + dict({ + 'docstring': dict({ + 'description': '', + 'full_docstring': '', + }), + 'id': 'various_modules_package/infer_types_module/InferMyTypes/_', + 'is_class_method': False, + 'is_property': False, + 'is_public': False, + 'is_static': False, + 'name': '_', + 'parameters': list([ + 'various_modules_package/infer_types_module/InferMyTypes/_/self', + ]), + 'reexported_by': list([ + ]), + 'results': list([ + 'various_modules_package/infer_types_module/InferMyTypes/_/result_1', + ]), + }), + dict({ + 'docstring': dict({ + 'description': '', + 'full_docstring': '', + }), + 'id': 'various_modules_package/infer_types_module/InferMyTypes/infer_call_result_1', + 'is_class_method': False, + 'is_property': False, + 'is_public': True, + 'is_static': False, + 'name': 'infer_call_result_1', + 'parameters': list([ + 'various_modules_package/infer_types_module/InferMyTypes/infer_call_result_1/self', + ]), + 'reexported_by': list([ + ]), + 'results': list([ + ]), + }), + dict({ + 'docstring': dict({ + 'description': '', + 'full_docstring': '', + }), + 'id': 'various_modules_package/infer_types_module/InferMyTypes/infer_call_result_2', + 'is_class_method': False, + 'is_property': False, + 'is_public': True, + 'is_static': False, + 'name': 'infer_call_result_2', + 'parameters': list([ + 'various_modules_package/infer_types_module/InferMyTypes/infer_call_result_2/self', + ]), + 'reexported_by': list([ + ]), + 'results': list([ + ]), + }), dict({ 'docstring': dict({ 'description': '', @@ -2176,6 +2249,9 @@ 'various_modules_package/infer_types_module/InferMyTypes/infer_param_types', 'various_modules_package/infer_types_module/InferMyTypes/infer_function', 'various_modules_package/infer_types_module/InferMyTypes/infer_function_2', + 'various_modules_package/infer_types_module/InferMyTypes/infer_call_result_1', + 'various_modules_package/infer_types_module/InferMyTypes/_', + 'various_modules_package/infer_types_module/InferMyTypes/infer_call_result_2', ]), 'name': 'InferMyTypes', 'reexported_by': list([ @@ -4277,6 +4353,31 @@ }), ]) # --- +# name: test_function_parameters[type_var_func] + list([ + dict({ + 'assigned_by': 'POSITION_OR_NAME', + 'default_value': None, + 'docstring': dict({ + 'default_value': '', + 'description': '', + 'type': '', + }), + 'id': 'various_modules_package/function_module/type_var_func/type_var_list', + 'is_optional': False, + 'name': 'type_var_list', + 'type': dict({ + 'kind': 'ListType', + 'types': list([ + dict({ + 'kind': 'TypeVarType', + 'name': '_type_var', + }), + ]), + }), + }), + ]) +# --- # name: test_function_results[abstract_method_params] list([ dict({ @@ -4995,6 +5096,27 @@ }), ]) # --- +# name: test_function_results[type_var_func] + list([ + dict({ + 'docstring': dict({ + 'description': '', + 'type': '', + }), + 'id': 'various_modules_package/function_module/type_var_func/result_1', + 'name': 'result_1', + 'type': dict({ + 'kind': 'ListType', + 'types': list([ + dict({ + 'kind': 'TypeVarType', + 'name': '_type_var', + }), + ]), + }), + }), + ]) +# --- # name: test_function_results[union_dictionary_results] list([ dict({ @@ -5816,6 +5938,26 @@ 'various_modules_package/function_module/tuple_results/result_2', ]), }), + dict({ + 'docstring': dict({ + 'description': '', + 'full_docstring': '', + }), + 'id': 'various_modules_package/function_module/type_var_func', + 'is_class_method': False, + 'is_property': False, + 'is_public': True, + 'is_static': False, + 'name': 'type_var_func', + 'parameters': list([ + 'various_modules_package/function_module/type_var_func/type_var_list', + ]), + 'reexported_by': list([ + ]), + 'results': list([ + 'various_modules_package/function_module/type_var_func/result_1', + ]), + }), dict({ 'docstring': dict({ 'description': '', diff --git a/tests/safeds_stubgen/api_analyzer/test__get_api.py b/tests/safeds_stubgen/api_analyzer/test__get_api.py index 022d53bf..14d5fb61 100644 --- a/tests/safeds_stubgen/api_analyzer/test__get_api.py +++ b/tests/safeds_stubgen/api_analyzer/test__get_api.py @@ -389,6 +389,7 @@ def test_class_methods(module_name: str, class_name: str, docstring_style: str, ("arg", _function_module_name, "", "plaintext"), ("args_type", _function_module_name, "", "plaintext"), ("callable_type", _function_module_name, "", "plaintext"), + ("type_var_func", _function_module_name, "", "plaintext"), ("abstract_method_params", _abstract_module_name, "AbstractModuleClass", "plaintext"), ("abstract_static_method_params", _abstract_module_name, "AbstractModuleClass", "plaintext"), ("abstract_property_method", _abstract_module_name, "AbstractModuleClass", "plaintext"), @@ -418,6 +419,7 @@ def test_class_methods(module_name: str, class_name: str, docstring_style: str, "arg", "args_type", "callable_type", + "type_var_func", "abstract_method_params", "abstract_static_method_params", "abstract_property_method", @@ -475,6 +477,7 @@ def test_function_parameters( ("literal_results", _function_module_name, "", "plaintext"), ("any_results", _function_module_name, "", "plaintext"), ("callable_type", _function_module_name, "", "plaintext"), + ("type_var_func", _function_module_name, "", "plaintext"), ("instance_method", _function_module_name, "FunctionModuleClassB", "plaintext"), ("static_method_params", _function_module_name, "FunctionModuleClassB", "plaintext"), ("class_method_params", _function_module_name, "FunctionModuleClassB", "plaintext"), @@ -513,6 +516,7 @@ def test_function_parameters( "literal_results", "any_results", "callable_type", + "type_var_func", "instance_method", "static_method_params", "class_method_params", diff --git a/tests/safeds_stubgen/api_analyzer/test_api_visitor.py b/tests/safeds_stubgen/api_analyzer/test_api_visitor.py index d4bcc389..396ae3ad 100644 --- a/tests/safeds_stubgen/api_analyzer/test_api_visitor.py +++ b/tests/safeds_stubgen/api_analyzer/test_api_visitor.py @@ -11,20 +11,20 @@ @pytest.mark.parametrize( - ("qname", "expected_id", "package_name"), + ("module_path", "expected_id", "package_name"), [ ( - "some.path.package_name.src.data", + "path\\to\\some\\path\\package_name\\src\\data", "package_name/src/data", "package_name", ), ( - "some.path.package_name", + "path\\to\\some\\path\\package_name", "package_name", "package_name", ), ( - "some.path.no_package", + "path\\to\\some\\path\\no_package", "", "package_name", ), @@ -34,17 +34,17 @@ "package_name", ), ( - "some.package_name.package_name.src.data", + "path\\to\\some\\package_name\\package_name\\src\\data", "package_name/package_name/src/data", "package_name", ), ( - "some.path.package_name.src.package_name", + "path\\to\\some\\path\\package_name\\src\\package_name", "package_name/src/package_name", "package_name", ), ( - "some.package_name.package_name.src.package_name", + "path\\to\\some\\package_name\\package_name\\src\\package_name", "package_name/package_name/src/package_name", "package_name", ), @@ -59,7 +59,7 @@ "Package name twice in qname 3", ], ) -def test__create_module_id(qname: str, expected_id: str, package_name: str) -> None: +def test__create_module_id(module_path: str, expected_id: str, package_name: str) -> None: api = API( distribution="dist_name", package=package_name, @@ -68,8 +68,8 @@ def test__create_module_id(qname: str, expected_id: str, package_name: str) -> N visitor = MyPyAstVisitor(PlaintextDocstringParser(), api) if not expected_id: - with pytest.raises(ValueError, match="Package name could not be found in the qualified name of the module."): - visitor._create_module_id(qname) + with pytest.raises(ValueError, match="Package name could not be found in the module path."): + visitor._create_module_id(module_path) else: - module_id = visitor._create_module_id(qname) + module_id = visitor._create_module_id(module_path) assert module_id == expected_id diff --git a/tests/safeds_stubgen/stubs_generator/__snapshots__/test_generate_stubs.ambr b/tests/safeds_stubgen/stubs_generator/__snapshots__/test_generate_stubs.ambr index 4bd5b29e..5776ea99 100644 --- a/tests/safeds_stubgen/stubs_generator/__snapshots__/test_generate_stubs.ambr +++ b/tests/safeds_stubgen/stubs_generator/__snapshots__/test_generate_stubs.ambr @@ -46,6 +46,7 @@ from typing import Optional from typing import Final from typing import Literal + from typing import TypeVar class AttributesClassA() @@ -131,6 +132,8 @@ static attr multiAttr7: String @PythonName("multi_attr_8") static attr multiAttr8: String + @PythonName("type_var") + static attr typeVar: type_var @PythonName("init_attr") attr initAttr: Boolean @@ -228,6 +231,7 @@ from typing import Optional from typing import Literal from typing import Any + from typing import TypeVar // TODO Result type information missing. @Pure @@ -456,6 +460,12 @@ param: (a: String) -> (b: Int, c: String) ) -> result1: (a: Int, b: Int) -> c: Int + @Pure + @PythonName("type_var_func") + fun typeVarFunc( + @PythonName("type_var_list") typeVarList: List<_type_var> + ) -> result1: List<_type_var> + class FunctionModuleClassA() // TODO Some parameter have no type information. @@ -582,6 +592,16 @@ static fun inferFunction2( i: Int = 2 ) -> result1: union + + // TODO Result type information missing. + @Pure + @PythonName("infer_call_result_1") + fun inferCallResult1() + + // TODO Result type information missing. + @Pure + @PythonName("infer_call_result_2") + fun inferCallResult2() } ''' From 25be59565b9abeee07551042152412e75cc7583e Mon Sep 17 00:00:00 2001 From: Arsam Date: Sun, 10 Dec 2023 20:26:36 +0100 Subject: [PATCH 02/10] Linter fixes --- src/safeds_stubgen/api_analyzer/_ast_visitor.py | 2 +- src/safeds_stubgen/api_analyzer/_get_api.py | 14 +++++++++----- 2 files changed, 10 insertions(+), 6 deletions(-) diff --git a/src/safeds_stubgen/api_analyzer/_ast_visitor.py b/src/safeds_stubgen/api_analyzer/_ast_visitor.py index fac88cfc..0e8caebe 100644 --- a/src/safeds_stubgen/api_analyzer/_ast_visitor.py +++ b/src/safeds_stubgen/api_analyzer/_ast_visitor.py @@ -561,7 +561,7 @@ def _create_attribute( qname = getattr(attribute, "fullname", "") # Get node information - type_ = None + type_: sds_types.AbstractType | None = None node = None if hasattr(attribute, "node"): if not isinstance(attribute.node, mp_nodes.Var): diff --git a/src/safeds_stubgen/api_analyzer/_get_api.py b/src/safeds_stubgen/api_analyzer/_get_api.py index a9eb628c..85b59428 100644 --- a/src/safeds_stubgen/api_analyzer/_get_api.py +++ b/src/safeds_stubgen/api_analyzer/_get_api.py @@ -89,14 +89,18 @@ def _get_mypy_ast(files: list[str], package_paths: list[Path], root: Path) -> li init_results = [] for graph_key in graph_keys: graph = graphs[graph_key] - abs_path = graph.abspath + graph_path = graph.abspath - if root_path not in abs_path or not abs_path.endswith(".py"): + if graph_path is None: # pragma: no cover + raise ValueError("Could not parse path of a module.") + + tree = graph.tree + if tree is None or root_path not in graph_path or not graph_path.endswith(".py"): continue - if abs_path.endswith("__init__.py"): - init_results.append(graph.tree) + if graph_path.endswith("__init__.py"): + init_results.append(tree) else: - results.append(graph.tree) + results.append(tree) return init_results + results From 9f01b8bb4c2c8037f5a3d425c0a11dbb6e6f315c Mon Sep 17 00:00:00 2001 From: Arsam Date: Sun, 10 Dec 2023 20:37:47 +0100 Subject: [PATCH 03/10] Codecov & bug fixes --- src/safeds_stubgen/api_analyzer/__init__.py | 2 ++ .../api_analyzer/_ast_visitor.py | 2 +- src/safeds_stubgen/api_analyzer/_types.py | 5 +++-- .../safeds_stubgen/api_analyzer/test_types.py | 18 ++++++++++++++++++ 4 files changed, 24 insertions(+), 3 deletions(-) diff --git a/src/safeds_stubgen/api_analyzer/__init__.py b/src/safeds_stubgen/api_analyzer/__init__.py index 575a2556..5a35b0d6 100644 --- a/src/safeds_stubgen/api_analyzer/__init__.py +++ b/src/safeds_stubgen/api_analyzer/__init__.py @@ -31,6 +31,7 @@ NamedType, SetType, TupleType, + TypeVarType, UnionType, ) @@ -63,6 +64,7 @@ "Result", "SetType", "TupleType", + "TypeVarType", "UnionType", "VarianceKind", "WildcardImport", diff --git a/src/safeds_stubgen/api_analyzer/_ast_visitor.py b/src/safeds_stubgen/api_analyzer/_ast_visitor.py index 0e8caebe..b88cd1c5 100644 --- a/src/safeds_stubgen/api_analyzer/_ast_visitor.py +++ b/src/safeds_stubgen/api_analyzer/_ast_visitor.py @@ -568,7 +568,7 @@ def _create_attribute( # In this case we have a TypeVar attribute attr_name = getattr(attribute, "name", "") - if not attr_name: # pragma: no Cover + if not attr_name: # pragma: no cover raise AttributeError("Expected TypeVar to have attribute 'name'.") type_ = sds_types.TypeVarType(attr_name) diff --git a/src/safeds_stubgen/api_analyzer/_types.py b/src/safeds_stubgen/api_analyzer/_types.py index fc63910e..c90b84a5 100644 --- a/src/safeds_stubgen/api_analyzer/_types.py +++ b/src/safeds_stubgen/api_analyzer/_types.py @@ -35,12 +35,13 @@ def from_dict(cls, d: dict[str, Any]) -> AbstractType: return UnionType.from_dict(d) case CallableType.__name__: return CallableType.from_dict(d) + case TypeVarType.__name__: + return TypeVarType.from_dict(d) case _: raise ValueError(f"Cannot parse {d['kind']} value.") @abstractmethod - def to_dict(self) -> dict[str, Any]: - pass + def to_dict(self) -> dict[str, Any]: ... @dataclass(frozen=True) diff --git a/tests/safeds_stubgen/api_analyzer/test_types.py b/tests/safeds_stubgen/api_analyzer/test_types.py index f0ec2524..c94df331 100644 --- a/tests/safeds_stubgen/api_analyzer/test_types.py +++ b/tests/safeds_stubgen/api_analyzer/test_types.py @@ -16,6 +16,7 @@ ParameterAssignment, SetType, TupleType, + TypeVarType, UnionType, ) from safeds_stubgen.docstring_parsing import AttributeDocstring, ParameterDocstring @@ -237,6 +238,23 @@ def test_literal_type() -> None: assert hash(LiteralType(["a"])) != hash(LiteralType(["b"])) +def test_type_var_type() -> None: + type_ = TypeVarType("_T") + type_dict = { + "kind": "TypeVarType", + "name": "_T", + } + + assert AbstractType.from_dict(type_dict) == type_ + assert TypeVarType.from_dict(type_dict) == type_ + assert type_.to_dict() == type_dict + + assert TypeVarType("a") == TypeVarType("a") + assert hash(TypeVarType("a")) == hash(TypeVarType("a")) + assert TypeVarType("a") != TypeVarType("b") + assert hash(TypeVarType("a")) != hash(TypeVarType("b")) + + def test_final_type() -> None: type_ = FinalType(NamedType("some_type")) type_dict = { From 7039e44d0948168fc118860d18aee2bdbe2c8e8d Mon Sep 17 00:00:00 2001 From: Arsam Date: Sun, 10 Dec 2023 20:41:20 +0100 Subject: [PATCH 04/10] Refactoring --- src/safeds_stubgen/api_analyzer/_get_api.py | 15 +++------------ .../docstring_parsing/test_epydoc_parser.py | 1 - .../docstring_parsing/test_get_full_docstring.py | 1 - .../docstring_parsing/test_googledoc_parser.py | 1 - .../docstring_parsing/test_numpydoc_parser.py | 1 - .../test_plaintext_docstring_parser.py | 1 - .../docstring_parsing/test_restdoc_parser.py | 1 - 7 files changed, 3 insertions(+), 18 deletions(-) diff --git a/src/safeds_stubgen/api_analyzer/_get_api.py b/src/safeds_stubgen/api_analyzer/_get_api.py index 85b59428..3e3b1cc7 100644 --- a/src/safeds_stubgen/api_analyzer/_get_api.py +++ b/src/safeds_stubgen/api_analyzer/_get_api.py @@ -1,7 +1,6 @@ from __future__ import annotations import logging -from pathlib import Path from typing import TYPE_CHECKING import mypy.build as mypy_build @@ -15,6 +14,7 @@ from ._package_metadata import distribution, distribution_version, package_root if TYPE_CHECKING: + from pathlib import Path from mypy.nodes import MypyFile @@ -39,7 +39,6 @@ def get_api( walker = ASTWalker(callable_visitor) walkable_files = [] - package_paths = [] for file_path in root.glob(pattern="./**/*.py"): logging.info( "Working on file {posix_path}", @@ -51,24 +50,16 @@ def get_api( logging.info("Skipping test file") continue - # Check if the current file is an init file - if file_path.parts[-1] == "__init__.py": - # if a directory contains an __init__.py file it's a package - package_paths.append( - file_path.parent, - ) - continue - walkable_files.append(str(file_path)) - mypy_trees = _get_mypy_ast(walkable_files, package_paths, root) + mypy_trees = _get_mypy_ast(walkable_files, root) for tree in mypy_trees: walker.walk(tree) return callable_visitor.api -def _get_mypy_ast(files: list[str], package_paths: list[Path], root: Path) -> list[MypyFile]: +def _get_mypy_ast(files: list[str], root: Path) -> list[MypyFile]: if not files: raise ValueError("No files found to analyse.") diff --git a/tests/safeds_stubgen/docstring_parsing/test_epydoc_parser.py b/tests/safeds_stubgen/docstring_parsing/test_epydoc_parser.py index 0a001ff0..519a5709 100644 --- a/tests/safeds_stubgen/docstring_parsing/test_epydoc_parser.py +++ b/tests/safeds_stubgen/docstring_parsing/test_epydoc_parser.py @@ -24,7 +24,6 @@ files=[ str(Path(_test_dir / "data" / "docstring_parser_package" / "epydoc.py")), ], - package_paths=[], root=Path(_test_dir / "data" / "docstring_parser_package"), )[0] diff --git a/tests/safeds_stubgen/docstring_parsing/test_get_full_docstring.py b/tests/safeds_stubgen/docstring_parsing/test_get_full_docstring.py index 9db26201..7942444e 100644 --- a/tests/safeds_stubgen/docstring_parsing/test_get_full_docstring.py +++ b/tests/safeds_stubgen/docstring_parsing/test_get_full_docstring.py @@ -19,7 +19,6 @@ files=[ str(Path(_test_dir / "data" / "docstring_parser_package" / "full_docstring.py")), ], - package_paths=[], root=Path(_test_dir / "data" / "docstring_parser_package"), )[0] diff --git a/tests/safeds_stubgen/docstring_parsing/test_googledoc_parser.py b/tests/safeds_stubgen/docstring_parsing/test_googledoc_parser.py index e8d95a2a..71c4997e 100644 --- a/tests/safeds_stubgen/docstring_parsing/test_googledoc_parser.py +++ b/tests/safeds_stubgen/docstring_parsing/test_googledoc_parser.py @@ -27,7 +27,6 @@ files=[ str(Path(_test_dir / "data" / "docstring_parser_package" / "googledoc.py")), ], - package_paths=[], root=Path(_test_dir / "data" / "docstring_parser_package"), )[0] diff --git a/tests/safeds_stubgen/docstring_parsing/test_numpydoc_parser.py b/tests/safeds_stubgen/docstring_parsing/test_numpydoc_parser.py index f517c668..471f2dd8 100644 --- a/tests/safeds_stubgen/docstring_parsing/test_numpydoc_parser.py +++ b/tests/safeds_stubgen/docstring_parsing/test_numpydoc_parser.py @@ -28,7 +28,6 @@ files=[ str(Path(_test_dir / "data" / _test_package_name / "numpydoc.py")), ], - package_paths=[], root=Path(_test_dir / "data" / _test_package_name), )[0] diff --git a/tests/safeds_stubgen/docstring_parsing/test_plaintext_docstring_parser.py b/tests/safeds_stubgen/docstring_parsing/test_plaintext_docstring_parser.py index 87a93c31..9c283de3 100644 --- a/tests/safeds_stubgen/docstring_parsing/test_plaintext_docstring_parser.py +++ b/tests/safeds_stubgen/docstring_parsing/test_plaintext_docstring_parser.py @@ -26,7 +26,6 @@ files=[ str(Path(_test_dir / "data" / "docstring_parser_package" / "plaintext.py")), ], - package_paths=[], root=Path(_test_dir / "data" / "docstring_parser_package"), )[0] diff --git a/tests/safeds_stubgen/docstring_parsing/test_restdoc_parser.py b/tests/safeds_stubgen/docstring_parsing/test_restdoc_parser.py index 8f8fff51..7c82e831 100644 --- a/tests/safeds_stubgen/docstring_parsing/test_restdoc_parser.py +++ b/tests/safeds_stubgen/docstring_parsing/test_restdoc_parser.py @@ -24,7 +24,6 @@ files=[ str(Path(_test_dir / "data" / "docstring_parser_package" / "restdoc.py")), ], - package_paths=[], root=Path(_test_dir / "data" / "docstring_parser_package"), )[0] From 3a3450dff5ade1e4d8ed21a8a4ddbf6bac0f2fa5 Mon Sep 17 00:00:00 2001 From: megalinter-bot <129584137+megalinter-bot@users.noreply.github.com> Date: Sun, 10 Dec 2023 19:42:50 +0000 Subject: [PATCH 05/10] style: apply automated linter fixes --- src/safeds_stubgen/api_analyzer/_ast_visitor.py | 4 ++-- src/safeds_stubgen/api_analyzer/_get_api.py | 1 + src/safeds_stubgen/api_analyzer/_types.py | 1 + 3 files changed, 4 insertions(+), 2 deletions(-) diff --git a/src/safeds_stubgen/api_analyzer/_ast_visitor.py b/src/safeds_stubgen/api_analyzer/_ast_visitor.py index b88cd1c5..665cca96 100644 --- a/src/safeds_stubgen/api_analyzer/_ast_visitor.py +++ b/src/safeds_stubgen/api_analyzer/_ast_visitor.py @@ -797,12 +797,12 @@ def _create_module_id(self, module_path: str) -> str: # We have to split the qname of the module at the first occurence of the package name and reconnect it while # discarding everything in front of it. This is necessary since the qname could contain unwanted information. module_id = module_path.split(package_name, 1)[-1] - module_id = module_id.replace("\\", '/') + module_id = module_id.replace("\\", "/") if module_id.startswith("/"): module_id = module_id[1:] - if module_id.endswith('.py'): + if module_id.endswith(".py"): module_id = module_id[:-3] if module_id: diff --git a/src/safeds_stubgen/api_analyzer/_get_api.py b/src/safeds_stubgen/api_analyzer/_get_api.py index 3e3b1cc7..0f578a1c 100644 --- a/src/safeds_stubgen/api_analyzer/_get_api.py +++ b/src/safeds_stubgen/api_analyzer/_get_api.py @@ -15,6 +15,7 @@ if TYPE_CHECKING: from pathlib import Path + from mypy.nodes import MypyFile diff --git a/src/safeds_stubgen/api_analyzer/_types.py b/src/safeds_stubgen/api_analyzer/_types.py index c90b84a5..f98174c9 100644 --- a/src/safeds_stubgen/api_analyzer/_types.py +++ b/src/safeds_stubgen/api_analyzer/_types.py @@ -403,6 +403,7 @@ def to_dict(self) -> dict[str, str]: def __hash__(self) -> int: return hash(frozenset([self.name])) + # ############################## Utilities ############################## # # def _dismantel_type_string_structure(type_structure: str) -> list: # current_type = "" From a92e815babf37271a1f37c61c62ea6bdc5898844 Mon Sep 17 00:00:00 2001 From: megalinter-bot <129584137+megalinter-bot@users.noreply.github.com> Date: Mon, 19 Feb 2024 11:50:13 +0000 Subject: [PATCH 06/10] style: apply automated linter fixes --- package-lock.json | 20 +++++++++---------- package.json | 2 +- .../api_analyzer/_ast_visitor.py | 6 +++--- 3 files changed, 14 insertions(+), 14 deletions(-) diff --git a/package-lock.json b/package-lock.json index 2fbb12ea..c4ccb3c3 100644 --- a/package-lock.json +++ b/package-lock.json @@ -8,7 +8,7 @@ "name": "safe-ds-stub-generator", "version": "0.0.1", "devDependencies": { - "@lars-reimann/prettier-config": "^5.0.0", + "@lars-reimann/prettier-config": "^5.2.1", "@semantic-release/changelog": "^6.0.3", "@semantic-release/exec": "^6.0.3", "@semantic-release/git": "^10.0.1", @@ -63,12 +63,12 @@ } }, "node_modules/@lars-reimann/prettier-config": { - "version": "5.0.0", - "resolved": "https://registry.npmjs.org/@lars-reimann/prettier-config/-/prettier-config-5.0.0.tgz", - "integrity": "sha512-52Ha8xMKpQESiaEzceWgyQb+fuPVD3wl2p6Op1mpLyLj6natjq7Vy8lAmbWS3AbPRjPlJZZHnp/b+sOAOdNqbA==", + "version": "5.2.1", + "resolved": "https://registry.npmjs.org/@lars-reimann/prettier-config/-/prettier-config-5.2.1.tgz", + "integrity": "sha512-mZv2ZmWHDoibDb07k+DOFHlcAKNygaWJsvlTPaWW4jPuo/vjULnlk6kNxGnEoFXz9Lk7rIjOUHd/Nk5csWKPEQ==", "dev": true, "peerDependencies": { - "prettier": ">= 2" + "prettier": "^3.2.5" } }, "node_modules/@nodelib/fs.scandir": { @@ -6053,16 +6053,16 @@ } }, "node_modules/prettier": { - "version": "2.8.4", - "resolved": "https://registry.npmjs.org/prettier/-/prettier-2.8.4.tgz", - "integrity": "sha512-vIS4Rlc2FNh0BySk3Wkd6xmwxB0FpOndW5fisM5H8hsZSxU2VWVB5CWIkIjWvrHjIhxk2g3bfMKM87zNTrZddw==", + "version": "3.2.5", + "resolved": "https://registry.npmjs.org/prettier/-/prettier-3.2.5.tgz", + "integrity": "sha512-3/GWa9aOC0YeD7LUfvOG2NiDyhOWRvt1k+rcKhOuYnMY24iiCphgneUfJDyFXd6rZCAnuLBv6UeAULtrhT/F4A==", "dev": true, "peer": true, "bin": { - "prettier": "bin-prettier.js" + "prettier": "bin/prettier.cjs" }, "engines": { - "node": ">=10.13.0" + "node": ">=14" }, "funding": { "url": "https://github.com/prettier/prettier?sponsor=1" diff --git a/package.json b/package.json index 4a791496..1614ad03 100644 --- a/package.json +++ b/package.json @@ -4,7 +4,7 @@ "private": true, "prettier": "@lars-reimann/prettier-config", "devDependencies": { - "@lars-reimann/prettier-config": "^5.0.0", + "@lars-reimann/prettier-config": "^5.2.1", "@semantic-release/changelog": "^6.0.3", "@semantic-release/exec": "^6.0.3", "@semantic-release/git": "^10.0.1", diff --git a/src/safeds_stubgen/api_analyzer/_ast_visitor.py b/src/safeds_stubgen/api_analyzer/_ast_visitor.py index 665cca96..b291b483 100644 --- a/src/safeds_stubgen/api_analyzer/_ast_visitor.py +++ b/src/safeds_stubgen/api_analyzer/_ast_visitor.py @@ -150,9 +150,9 @@ def enter_classdef(self, node: mp_nodes.ClassDef) -> None: variance_type = mypy_variance_parser(generic_type.variance) variance_values: sds_types.AbstractType if variance_type == VarianceKind.INVARIANT: - variance_values = sds_types.UnionType([ - mypy_type_to_abstract_type(value) for value in generic_type.values - ]) + variance_values = sds_types.UnionType( + [mypy_type_to_abstract_type(value) for value in generic_type.values], + ) else: variance_values = mypy_type_to_abstract_type(generic_type.upper_bound) From b3f1612bea42379a0e8a1d1f04ab4ffbab73214f Mon Sep 17 00:00:00 2001 From: Arsam Date: Mon, 19 Feb 2024 13:56:48 +0100 Subject: [PATCH 07/10] Removed a todo --- src/safeds_stubgen/api_analyzer/_types.py | 1 - 1 file changed, 1 deletion(-) diff --git a/src/safeds_stubgen/api_analyzer/_types.py b/src/safeds_stubgen/api_analyzer/_types.py index f98174c9..9d7bbc7e 100644 --- a/src/safeds_stubgen/api_analyzer/_types.py +++ b/src/safeds_stubgen/api_analyzer/_types.py @@ -388,7 +388,6 @@ def __hash__(self) -> int: return hash(frozenset(self.types)) -# Todo Frage: Wie stellen wir TypeVar dar? @dataclass(frozen=True) class TypeVarType(AbstractType): name: str From 42b7b78d52292514339eb57ad8a0fbff94d5206e Mon Sep 17 00:00:00 2001 From: Arsam Date: Tue, 20 Feb 2024 14:23:22 +0100 Subject: [PATCH 08/10] Fixed stub generation if a function return statement is a condition expression --- .../api_analyzer/_ast_visitor.py | 25 ++++++++++++++----- .../function_module.py | 4 +++ .../__snapshots__/test__get_api.ambr | 19 ++++++++++++++ .../__snapshots__/test_generate_stubs.ambr | 4 +++ 4 files changed, 46 insertions(+), 6 deletions(-) diff --git a/src/safeds_stubgen/api_analyzer/_ast_visitor.py b/src/safeds_stubgen/api_analyzer/_ast_visitor.py index b291b483..b22c1b23 100644 --- a/src/safeds_stubgen/api_analyzer/_ast_visitor.py +++ b/src/safeds_stubgen/api_analyzer/_ast_visitor.py @@ -418,14 +418,27 @@ def _infer_type_from_return_stmts(func_node: mp_nodes.FuncDef) -> sds_types.Name func_defn = get_funcdef_definitions(func_node) return_stmts = find_return_stmts_recursive(func_defn) if return_stmts: - # In this case the items of the types set can only be of the class "NamedType" or "TupleType" but we have to - # make a typecheck anyway for the mypy linter. types = set() for return_stmt in return_stmts: - if return_stmt.expr is not None and not isinstance(return_stmt.expr, mp_nodes.CallExpr): - type_ = mypy_expression_to_sds_type(return_stmt.expr) - if isinstance(type_, sds_types.NamedType | sds_types.TupleType): - types.add(type_) + if return_stmt.expr is None: + continue + + if not isinstance(return_stmt.expr, mp_nodes.CallExpr | mp_nodes.MemberExpr): + # Todo Frage: Parse conditional branches recursively? + # If the return statement is a conditional expression we parse the "if" and "else" branches + if isinstance(return_stmt.expr, mp_nodes.ConditionalExpr): + for conditional_branch in [return_stmt.expr.if_expr, return_stmt.expr.else_expr]: + if conditional_branch is None: + continue + + if not isinstance(conditional_branch, mp_nodes.CallExpr | mp_nodes.MemberExpr): + type_ = mypy_expression_to_sds_type(conditional_branch) + if isinstance(type_, sds_types.NamedType | sds_types.TupleType): + types.add(type_) + else: + type_ = mypy_expression_to_sds_type(return_stmt.expr) + if isinstance(type_, sds_types.NamedType | sds_types.TupleType): + types.add(type_) # We have to sort the list for the snapshot tests return_stmt_types = list(types) diff --git a/tests/data/various_modules_package/function_module.py b/tests/data/various_modules_package/function_module.py index 7f114d0d..45b7e6ed 100644 --- a/tests/data/various_modules_package/function_module.py +++ b/tests/data/various_modules_package/function_module.py @@ -193,3 +193,7 @@ def property_function_params(self) -> str: ... @property def property_function_infer(self): return "some string" + + +def ret_conditional_statement(): + return 1 if True else False 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 cb38566b..763203bd 100644 --- a/tests/safeds_stubgen/api_analyzer/__snapshots__/test__get_api.ambr +++ b/tests/safeds_stubgen/api_analyzer/__snapshots__/test__get_api.ambr @@ -5854,6 +5854,25 @@ 'results': list([ ]), }), + dict({ + 'docstring': dict({ + 'description': '', + 'full_docstring': '', + }), + 'id': 'various_modules_package/function_module/ret_conditional_statement', + 'is_class_method': False, + 'is_property': False, + 'is_public': True, + 'is_static': False, + 'name': 'ret_conditional_statement', + 'parameters': list([ + ]), + 'reexported_by': list([ + ]), + 'results': list([ + 'various_modules_package/function_module/ret_conditional_statement/result_1', + ]), + }), dict({ 'docstring': dict({ 'description': '', diff --git a/tests/safeds_stubgen/stubs_generator/__snapshots__/test_generate_stubs.ambr b/tests/safeds_stubgen/stubs_generator/__snapshots__/test_generate_stubs.ambr index 5776ea99..debf3bfa 100644 --- a/tests/safeds_stubgen/stubs_generator/__snapshots__/test_generate_stubs.ambr +++ b/tests/safeds_stubgen/stubs_generator/__snapshots__/test_generate_stubs.ambr @@ -466,6 +466,10 @@ @PythonName("type_var_list") typeVarList: List<_type_var> ) -> result1: List<_type_var> + @Pure + @PythonName("ret_conditional_statement") + fun retConditionalStatement() -> result1: union + class FunctionModuleClassA() // TODO Some parameter have no type information. From 00edbb1c2c568db947097964c03eca7d98f718bf Mon Sep 17 00:00:00 2001 From: Arsam Date: Tue, 20 Feb 2024 17:10:47 +0100 Subject: [PATCH 09/10] Codecov refactoring --- src/safeds_stubgen/api_analyzer/_ast_visitor.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/safeds_stubgen/api_analyzer/_ast_visitor.py b/src/safeds_stubgen/api_analyzer/_ast_visitor.py index b22c1b23..7593ea8e 100644 --- a/src/safeds_stubgen/api_analyzer/_ast_visitor.py +++ b/src/safeds_stubgen/api_analyzer/_ast_visitor.py @@ -420,7 +420,7 @@ def _infer_type_from_return_stmts(func_node: mp_nodes.FuncDef) -> sds_types.Name if return_stmts: types = set() for return_stmt in return_stmts: - if return_stmt.expr is None: + if return_stmt.expr is None: # pragma: no cover continue if not isinstance(return_stmt.expr, mp_nodes.CallExpr | mp_nodes.MemberExpr): @@ -428,7 +428,7 @@ def _infer_type_from_return_stmts(func_node: mp_nodes.FuncDef) -> sds_types.Name # If the return statement is a conditional expression we parse the "if" and "else" branches if isinstance(return_stmt.expr, mp_nodes.ConditionalExpr): for conditional_branch in [return_stmt.expr.if_expr, return_stmt.expr.else_expr]: - if conditional_branch is None: + if conditional_branch is None: # pragma: no cover continue if not isinstance(conditional_branch, mp_nodes.CallExpr | mp_nodes.MemberExpr): From 070100d2b828bd1c27dab953d4f67d9f0306afb7 Mon Sep 17 00:00:00 2001 From: Arsam Date: Wed, 21 Feb 2024 10:34:55 +0100 Subject: [PATCH 10/10] Removing TypeVar Types from the stubs generation --- src/safeds_stubgen/stubs_generator/_generate_stubs.py | 4 ++-- .../stubs_generator/__snapshots__/test_generate_stubs.ambr | 6 +++--- 2 files changed, 5 insertions(+), 5 deletions(-) diff --git a/src/safeds_stubgen/stubs_generator/_generate_stubs.py b/src/safeds_stubgen/stubs_generator/_generate_stubs.py index e9b50294..6e88bdb2 100644 --- a/src/safeds_stubgen/stubs_generator/_generate_stubs.py +++ b/src/safeds_stubgen/stubs_generator/_generate_stubs.py @@ -621,9 +621,9 @@ def _create_type_string(self, type_data: dict | None) -> str: else: types.append(f"{literal_type}") return f"literal<{', '.join(types)}>" - # Todo Frage: Wie stellen wir TypeVar dar? + # Todo See issue #63 elif kind == "TypeVarType": - return f"{type_data['name']}" + return "" raise ValueError(f"Unexpected type: {kind}") # pragma: no cover diff --git a/tests/safeds_stubgen/stubs_generator/__snapshots__/test_generate_stubs.ambr b/tests/safeds_stubgen/stubs_generator/__snapshots__/test_generate_stubs.ambr index debf3bfa..a10871d0 100644 --- a/tests/safeds_stubgen/stubs_generator/__snapshots__/test_generate_stubs.ambr +++ b/tests/safeds_stubgen/stubs_generator/__snapshots__/test_generate_stubs.ambr @@ -133,7 +133,7 @@ @PythonName("multi_attr_8") static attr multiAttr8: String @PythonName("type_var") - static attr typeVar: type_var + static attr typeVar @PythonName("init_attr") attr initAttr: Boolean @@ -463,8 +463,8 @@ @Pure @PythonName("type_var_func") fun typeVarFunc( - @PythonName("type_var_list") typeVarList: List<_type_var> - ) -> result1: List<_type_var> + @PythonName("type_var_list") typeVarList: List<> + ) -> result1: List<> @Pure @PythonName("ret_conditional_statement")