Skip to content

Commit

Permalink
feat: Combine multiple examples to one (#152)
Browse files Browse the repository at this point in the history
Closes #139

### Summary of Changes
Multiple example docstrings ~~are now combined to one big example
docstrings~~ will now each have an example block in Safe-DS docstrings.
Previously only the first docstring was saved and others where ignored.
  • Loading branch information
Masara authored Jun 20, 2024
1 parent d75983a commit fa09129
Show file tree
Hide file tree
Showing 7 changed files with 332 additions and 144 deletions.
4 changes: 2 additions & 2 deletions src/safeds_stubgen/docstring_parsing/_docstring.py
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,7 @@
class ClassDocstring:
description: str = ""
full_docstring: str = ""
example: str = ""
examples: list[str] = dataclasses.field(default_factory=list)

def to_dict(self) -> dict[str, Any]:
return dataclasses.asdict(self)
Expand All @@ -24,7 +24,7 @@ def to_dict(self) -> dict[str, Any]:
class FunctionDocstring:
description: str = ""
full_docstring: str = ""
example: str = ""
examples: list[str] = dataclasses.field(default_factory=list)

def to_dict(self) -> dict[str, Any]:
return dataclasses.asdict(self)
Expand Down
14 changes: 8 additions & 6 deletions src/safeds_stubgen/docstring_parsing/_docstring_parser.py
Original file line number Diff line number Diff line change
Expand Up @@ -50,39 +50,41 @@ def get_class_documentation(self, class_node: nodes.ClassDef) -> ClassDocstring:

description = ""
docstring = ""
example = ""
examples = []
if griffe_node.docstring is not None:
docstring = griffe_node.docstring.value.strip("\n")

for docstring_section in griffe_node.docstring.parsed:
if docstring_section.kind == DocstringSectionKind.text:
description = docstring_section.value.strip("\n")
elif docstring_section.kind == DocstringSectionKind.examples:
example = docstring_section.value[0][1].strip("\n")
for example_data in docstring_section.value:
examples.append(example_data[1].strip("\n"))

return ClassDocstring(
description=description,
full_docstring=docstring,
example=example,
examples=examples,
)

def get_function_documentation(self, function_node: nodes.FuncDef) -> FunctionDocstring:
docstring = ""
description = ""
example = ""
examples = []
griffe_docstring = self.__get_cached_docstring(function_node.fullname)
if griffe_docstring is not None:
docstring = griffe_docstring.value.strip("\n")
for docstring_section in griffe_docstring.parsed:
if docstring_section.kind == DocstringSectionKind.text:
description = docstring_section.value.strip("\n")
elif docstring_section.kind == DocstringSectionKind.examples:
example = docstring_section.value[0][1].strip("\n")
for example_data in docstring_section.value:
examples.append(example_data[1].strip("\n"))

return FunctionDocstring(
description=description,
full_docstring=docstring,
example=example,
examples=examples,
)

def get_parameter_documentation(
Expand Down
27 changes: 16 additions & 11 deletions src/safeds_stubgen/stubs_generator/_stub_string_generator.py
Original file line number Diff line number Diff line change
Expand Up @@ -948,18 +948,23 @@ def _create_sds_docstring(
full_docstring += full_result_docstring

# Example
example = ""
if not isinstance(docstring, AttributeDocstring) and docstring.example:
example = f"{indentations} * @example\n{indentations} * pipeline example {{\n"
for example_part in docstring.example.split("\n"):
if example_part.startswith(">>>"):
example += f"{indentations} * {example_part.replace('>>>', '//')}\n"
elif example_part.startswith("..."):
example += f"{indentations} * {example_part.replace('...', '//')}\n"
example += f"{indentations} * }}\n"
if full_docstring and example:
example_docstrings = []
if not isinstance(docstring, AttributeDocstring) and docstring.examples:
for example in docstring.examples:
example_text = f"{indentations} * @example\n{indentations} * pipeline example {{\n"
for example_part in example.split("\n"):
if example_part.startswith(">>>"):
example_text += f"{indentations} * {example_part.replace('>>>', '//')}\n"
elif example_part.startswith("..."):
example_text += f"{indentations} * {example_part.replace('...', '//')}\n"
example_text += f"{indentations} * }}\n"
example_docstrings.append(example_text)

if full_docstring and example_docstrings:
full_docstring += f"{indentations} *\n"
full_docstring += example

example_docstring = f"{indentations} *\n".join(example_docstrings)
full_docstring += example_docstring

# Open and close the docstring
if full_docstring:
Expand Down
26 changes: 26 additions & 0 deletions tests/data/docstring_parser_package/numpydoc.py
Original file line number Diff line number Diff line change
Expand Up @@ -550,6 +550,32 @@ def numpy_named_result_without_type_inferred():
return "result"


class ClassWithExample:
"""
Examples
--------
>>> print(1+1)
2
>>> x = 2 - 1
>>> print(x)
1
"""


def function_with_example() -> None:
"""
Examples
--------
>>> print(1+1)
2
>>> x = 2 - 1
>>> print(x)
1
"""


def numpy_named_result_with_more_hints_than_docstring_types() -> tuple[int, str]:
"""
numpy_named_result_without_type_inferred
Expand Down
54 changes: 36 additions & 18 deletions tests/safeds_stubgen/__snapshots__/test_main.ambr
Original file line number Diff line number Diff line change
Expand Up @@ -102,7 +102,8 @@
'constructor': None,
'docstring': dict({
'description': '',
'example': '',
'examples': list([
]),
'full_docstring': '',
}),
'id': 'tests/data/main_package/another_path/another_module/AnotherClass',
Expand All @@ -126,7 +127,8 @@
'constructor': None,
'docstring': dict({
'description': '',
'example': '',
'examples': list([
]),
'full_docstring': '',
}),
'id': 'tests/data/main_package/another_path/another_module/yetAnotherClass',
Expand Down Expand Up @@ -159,7 +161,8 @@

Full init description.
''',
'example': '',
'examples': list([
]),
'full_docstring': '''
Summary of the init description.

Expand Down Expand Up @@ -187,7 +190,8 @@

Full description
''',
'example': '',
'examples': list([
]),
'full_docstring': '''
Summary of the description.

Expand Down Expand Up @@ -217,7 +221,8 @@
'constructor': None,
'docstring': dict({
'description': '',
'example': '',
'examples': list([
]),
'full_docstring': '',
}),
'id': 'tests/data/main_package/main_module/ModuleClass/NestedClass',
Expand Down Expand Up @@ -246,7 +251,8 @@
'constructor': dict({
'docstring': dict({
'description': '',
'example': '',
'examples': list([
]),
'full_docstring': '',
}),
'id': 'tests/data/main_package/main_module/_PrivateClass/__init__',
Expand All @@ -265,7 +271,8 @@
}),
'docstring': dict({
'description': '',
'example': '',
'examples': list([
]),
'full_docstring': '',
}),
'id': 'tests/data/main_package/main_module/_PrivateClass',
Expand All @@ -292,7 +299,8 @@
'constructor': None,
'docstring': dict({
'description': '',
'example': '',
'examples': list([
]),
'full_docstring': '',
}),
'id': 'tests/data/main_package/main_module/_PrivateClass/NestedPrivateClass',
Expand All @@ -317,7 +325,8 @@
'constructor': None,
'docstring': dict({
'description': '',
'example': '',
'examples': list([
]),
'full_docstring': '',
}),
'id': 'tests/data/main_package/main_module/_PrivateClass/NestedPrivateClass/NestedNestedPrivateClass',
Expand All @@ -343,7 +352,8 @@
dict({
'docstring': dict({
'description': '',
'example': '',
'examples': list([
]),
'full_docstring': '',
}),
'id': 'tests/data/main_package/another_path/another_module/yetAnotherClass/another_function',
Expand All @@ -364,7 +374,8 @@
dict({
'docstring': dict({
'description': '',
'example': '',
'examples': list([
]),
'full_docstring': '',
}),
'id': 'tests/data/main_package/main_module/ModuleClass/NestedClass/nested_class_function',
Expand All @@ -390,7 +401,8 @@

Full init description.
''',
'example': '',
'examples': list([
]),
'full_docstring': '''
Summary of the init description.

Expand Down Expand Up @@ -419,7 +431,8 @@

param_2: bool.
''',
'example': '',
'examples': list([
]),
'full_docstring': '''
Function Docstring.

Expand All @@ -446,7 +459,8 @@
dict({
'docstring': dict({
'description': '',
'example': '',
'examples': list([
]),
'full_docstring': '',
}),
'id': 'tests/data/main_package/main_module/_PrivateClass/NestedPrivateClass/static_nested_private_class_function',
Expand All @@ -465,7 +479,8 @@
dict({
'docstring': dict({
'description': '',
'example': '',
'examples': list([
]),
'full_docstring': '',
}),
'id': 'tests/data/main_package/main_module/_PrivateClass/__init__',
Expand All @@ -485,7 +500,8 @@
dict({
'docstring': dict({
'description': '',
'example': '',
'examples': list([
]),
'full_docstring': '',
}),
'id': 'tests/data/main_package/main_module/_PrivateClass/public_func_in_private_class',
Expand All @@ -505,7 +521,8 @@
dict({
'docstring': dict({
'description': '',
'example': '',
'examples': list([
]),
'full_docstring': '',
}),
'id': 'tests/data/main_package/main_module/_private_global_func',
Expand All @@ -529,7 +546,8 @@

Docstring 2.
''',
'example': '',
'examples': list([
]),
'full_docstring': '''
Docstring 1.

Expand Down
Loading

0 comments on commit fa09129

Please sign in to comment.