Skip to content

Commit

Permalink
Merge branch 'main' into get-missing-type-hints-from-docstring
Browse files Browse the repository at this point in the history
  • Loading branch information
Masara committed Jun 21, 2024
2 parents 60107e0 + 4b03268 commit 380e9d2
Show file tree
Hide file tree
Showing 8 changed files with 68 additions and 6 deletions.
2 changes: 2 additions & 0 deletions src/safeds_stubgen/api_analyzer/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,7 @@
ParameterAssignment,
QualifiedImport,
Result,
UnknownValue,
VarianceKind,
WildcardImport,
)
Expand Down Expand Up @@ -74,6 +75,7 @@
"TypeVarType",
"UnionType",
"UnknownType",
"UnknownValue",
"VarianceKind",
"WildcardImport",
]
8 changes: 6 additions & 2 deletions src/safeds_stubgen/api_analyzer/_api.py
Original file line number Diff line number Diff line change
Expand Up @@ -262,13 +262,17 @@ def to_dict(self) -> dict[str, Any]:
}


class UnknownValue:
pass


@dataclass(frozen=True)
class Parameter:
id: str
name: str
is_optional: bool
# We do not support default values that aren't core classes or classes definied in the package we analyze.
default_value: str | bool | int | float | None
default_value: str | bool | int | float | None | UnknownValue
assigned_by: ParameterAssignment
docstring: ParameterDocstring
type: AbstractType | None
Expand All @@ -290,7 +294,7 @@ def to_dict(self) -> dict[str, Any]:
"name": self.name,
"docstring": self.docstring.to_dict(),
"is_optional": self.is_optional,
"default_value": self.default_value,
"default_value": "UnknownValue" if isinstance(self.default_value, UnknownValue) else self.default_value,
"assigned_by": self.assigned_by.name,
"type": self.type.to_dict() if self.type is not None else None,
}
Expand Down
28 changes: 25 additions & 3 deletions src/safeds_stubgen/api_analyzer/_ast_visitor.py
Original file line number Diff line number Diff line change
Expand Up @@ -25,6 +25,7 @@
QualifiedImport,
Result,
TypeParameter,
UnknownValue,
VarianceKind,
WildcardImport,
)
Expand Down Expand Up @@ -895,7 +896,7 @@ def _parse_parameter_data(self, node: mp_nodes.FuncDef, function_id: str) -> lis
# Get default value and infer type information
initializer = argument.initializer
if initializer is not None:
default_value, default_is_none = self._get_parameter_type_and_default_value(initializer)
default_value, default_is_none = self._get_parameter_type_and_default_value(initializer, function_id)
if arg_type is None and (default_is_none or default_value is not None):
arg_type = mypy_expression_to_sds_type(initializer)

Expand Down Expand Up @@ -928,10 +929,11 @@ def _parse_parameter_data(self, node: mp_nodes.FuncDef, function_id: str) -> lis

return arguments

@staticmethod
def _get_parameter_type_and_default_value(
self,
initializer: mp_nodes.Expression,
) -> tuple[str | None | int | float, bool]:
function_id: str,
) -> tuple[str | None | int | float | UnknownValue, bool]:
default_value: str | None | int | float = None
default_is_none = False
if initializer is not None:
Expand All @@ -940,8 +942,28 @@ def _get_parameter_type_and_default_value(
# in the package we analyze with Safe-DS.
return default_value, default_is_none
elif isinstance(initializer, mp_nodes.CallExpr):
msg = (
f"Could not parse parameter type for function {function_id}: Safe-DS does not support call "
f"expressions as types."
)
logging.warning(msg)
# Safe-DS does not support call expressions as types
return default_value, default_is_none
elif isinstance(initializer, mp_nodes.UnaryExpr):
value, default_is_none = self._get_parameter_type_and_default_value(initializer.expr, function_id)
try:
if isinstance(value, int):
return int(f"{initializer.op}{value}"), default_is_none
elif isinstance(value, float):
return float(f"{initializer.op}{value}"), default_is_none
except ValueError:
pass
msg = (
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)
return UnknownValue(), default_is_none
elif isinstance(
initializer,
mp_nodes.IntExpr | mp_nodes.FloatExpr | mp_nodes.StrExpr | mp_nodes.NameExpr,
Expand Down
2 changes: 2 additions & 0 deletions src/safeds_stubgen/api_analyzer/_mypy_helpers.py
Original file line number Diff line number Diff line change
Expand Up @@ -100,6 +100,8 @@ def mypy_expression_to_sds_type(expr: mp_nodes.Expression) -> sds_types.Abstract
return sds_types.NamedType(name="str", qname="builtins.str")
elif isinstance(expr, mp_nodes.TupleExpr):
return sds_types.TupleType(types=[mypy_expression_to_sds_type(item) for item in expr.items])
elif isinstance(expr, mp_nodes.UnaryExpr):
return mypy_expression_to_sds_type(expr.expr)

raise TypeError("Unexpected expression type.") # pragma: no cover

Expand Down
5 changes: 5 additions & 0 deletions src/safeds_stubgen/stubs_generator/_stub_string_generator.py
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,7 @@
ParameterAssignment,
Result,
UnionType,
UnknownValue,
VarianceKind,
result_name_generator,
)
Expand Down Expand Up @@ -595,6 +596,9 @@ def _create_parameter_string(
default_value = "true" if param_default_value else "false"
elif param_default_value is None:
default_value = "null"
elif isinstance(param_default_value, UnknownValue):
self._current_todo_msgs.add("unknown value")
default_value = "unknown"
else:
default_value = f"{param_default_value}"
param_value = f" = {default_value}"
Expand Down Expand Up @@ -1092,6 +1096,7 @@ def _create_todo_msg(self, indentations: str) -> str:
"result without type": "Result type information missing.",
"internal class as type": "An internal class must not be used as a type in a public class.",
"unknown": "Unknown type - Type could not be parsed.",
"unknown value": "Unknown value - Value could not be parsed.",
}[msg]
for msg in self._current_todo_msgs
]
Expand Down
3 changes: 3 additions & 0 deletions tests/data/various_modules_package/function_module.py
Original file line number Diff line number Diff line change
Expand Up @@ -68,6 +68,8 @@ def params(

def params_with_default_value(
integer: int = 3,
negative_int: int = -1,
negative_float: float = -2.3,
boolean: bool = True,
float_: float = 1.2,
none: None = None,
Expand Down Expand Up @@ -106,6 +108,7 @@ def special_params(
none_none_bool_none_union: None | None | bool | None,
none_list_union_none_none: None | list[None | None] | None,
none: None,
not_true=not 1
): ...


Expand Down
20 changes: 20 additions & 0 deletions tests/safeds_stubgen/api_analyzer/__snapshots__/test__get_api.ambr
Original file line number Diff line number Diff line change
Expand Up @@ -4878,6 +4878,23 @@
]),
}),
}),
dict({
'assigned_by': 'POSITION_OR_NAME',
'default_value': 'UnknownValue',
'docstring': dict({
'default_value': '',
'description': '',
'type': None,
}),
'id': 'tests/data/various_modules_package/function_module/special_params/not_true',
'is_optional': True,
'name': 'not_true',
'type': dict({
'kind': 'NamedType',
'name': 'int',
'qname': 'builtins.int',
}),
}),
])
# ---
# name: test_function_parameters[static_method_params]
Expand Down Expand Up @@ -6395,6 +6412,8 @@
'name': 'params_with_default_value',
'parameters': list([
'tests/data/various_modules_package/function_module/params_with_default_value/integer',
'tests/data/various_modules_package/function_module/params_with_default_value/negative_int',
'tests/data/various_modules_package/function_module/params_with_default_value/negative_float',
'tests/data/various_modules_package/function_module/params_with_default_value/boolean',
'tests/data/various_modules_package/function_module/params_with_default_value/float_',
'tests/data/various_modules_package/function_module/params_with_default_value/none',
Expand Down Expand Up @@ -6545,6 +6564,7 @@
'tests/data/various_modules_package/function_module/special_params/none_none_bool_none_union',
'tests/data/various_modules_package/function_module/special_params/none_list_union_none_none',
'tests/data/various_modules_package/function_module/special_params/none',
'tests/data/various_modules_package/function_module/special_params/not_true',
]),
'reexported_by': list([
]),
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -49,6 +49,8 @@ fun params(
@PythonName("params_with_default_value")
fun paramsWithDefaultValue(
integer: Int = 3,
@PythonName("negative_int") negativeInt: Int = -1,
@PythonName("negative_float") negativeFloat: Float = -2.3,
boolean: Boolean = true,
@PythonName("float_") float: Float = 1.2,
none: Nothing? = null,
Expand Down Expand Up @@ -81,6 +83,7 @@ fun illegalParams(
)

// TODO Result type information missing.
// TODO Unknown value - Value could not be parsed.
@Pure
@PythonName("special_params")
fun specialParams(
Expand All @@ -92,7 +95,8 @@ fun specialParams(
@PythonName("none_bool_int_union") noneBoolIntUnion: union<Boolean, Int, Nothing?>,
@PythonName("none_none_bool_none_union") noneNoneBoolNoneUnion: Boolean?,
@PythonName("none_list_union_none_none") noneListUnionNoneNone: List<Nothing?>?,
none: Nothing?
none: Nothing?,
@PythonName("not_true") notTrue: Int = unknown
)

// TODO Result type information missing.
Expand Down

0 comments on commit 380e9d2

Please sign in to comment.