Skip to content

Commit

Permalink
feat: Examples from docstrings are also taken over to stub docstrings (
Browse files Browse the repository at this point in the history
…#116)

Closes #115

### Summary of Changes

Examples from docstrings are also taken over to stub docstrings.
  • Loading branch information
Masara authored Apr 22, 2024
1 parent c7b8550 commit 6665186
Show file tree
Hide file tree
Showing 7 changed files with 200 additions and 3 deletions.
2 changes: 2 additions & 0 deletions src/safeds_stubgen/docstring_parsing/_docstring.py
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,7 @@
class ClassDocstring:
description: str = ""
full_docstring: str = ""
example: str = ""

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

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

description = ""
docstring = ""
example = ""
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")
break
elif docstring_section.kind == DocstringSectionKind.examples:
example = docstring_section.value[0][1].strip("\n")

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

def get_function_documentation(self, function_node: nodes.FuncDef) -> FunctionDocstring:
docstring = ""
description = ""
example = ""
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")
break
elif docstring_section.kind == DocstringSectionKind.examples:
example = docstring_section.value[0][1].strip("\n")

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

def get_parameter_documentation(
Expand Down
15 changes: 14 additions & 1 deletion src/safeds_stubgen/stubs_generator/_generate_stubs.py
Original file line number Diff line number Diff line change
Expand Up @@ -19,9 +19,10 @@
VarianceKind,
result_name_generator,
)
from safeds_stubgen.docstring_parsing import AttributeDocstring

if TYPE_CHECKING:
from safeds_stubgen.docstring_parsing import AttributeDocstring, ClassDocstring, FunctionDocstring
from safeds_stubgen.docstring_parsing import ClassDocstring, FunctionDocstring


INDENTATION = " "
Expand Down Expand Up @@ -930,6 +931,18 @@ def _create_sds_docstring(
full_result_docstring = f"{indentations} *\n{full_result_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"
example += f"{indentations} * }}\n"
if full_docstring and example:
full_docstring += f"{indentations} *\n"
full_docstring += example

# Open and close the docstring
if full_docstring:
full_docstring = f"{indentations}/**\n{full_docstring}{indentations} */\n"
Expand Down
25 changes: 25 additions & 0 deletions tests/data/docstring_parser_package/numpydoc.py
Original file line number Diff line number Diff line change
Expand Up @@ -456,3 +456,28 @@ def infer_types3(a, b):
if a:
return int
return True


class NumpyClassWithExamples:
"""
NumpyClassWithExamples.
Dolor sit amet.
Examples
--------
>>> from tests.data.docstring_parser_package.numpydoc import NumpyClassWithExamples
This text should be ignored
>>> class_ = NumpyClassWithExamples
This text should be ignored, too.
"""

def numpy_func_with_examples(self):
"""
Examples
--------
>>> from tests.data.docstring_parser_package.numpydoc import NumpyClassWithExamples
>>> func = NumpyClassWithExamples.numpy_func_with_examples
>>> func()
This text should be ignored.
"""
18 changes: 18 additions & 0 deletions tests/safeds_stubgen/__snapshots__/test_main.ambr
Original file line number Diff line number Diff line change
Expand Up @@ -102,6 +102,7 @@
'constructor': None,
'docstring': dict({
'description': '',
'example': '',
'full_docstring': '',
}),
'id': 'tests/data/main_package/another_path/another_module/AnotherClass',
Expand All @@ -125,6 +126,7 @@
'constructor': None,
'docstring': dict({
'description': '',
'example': '',
'full_docstring': '',
}),
'id': 'tests/data/main_package/another_path/another_module/yetAnotherClass',
Expand Down Expand Up @@ -157,6 +159,7 @@

Full init description.
''',
'example': '',
'full_docstring': '''
Summary of the init description.

Expand Down Expand Up @@ -184,6 +187,7 @@

Full description
''',
'example': '',
'full_docstring': '''
Summary of the description.

Expand Down Expand Up @@ -213,6 +217,7 @@
'constructor': None,
'docstring': dict({
'description': '',
'example': '',
'full_docstring': '',
}),
'id': 'tests/data/main_package/main_module/ModuleClass/NestedClass',
Expand Down Expand Up @@ -241,6 +246,7 @@
'constructor': dict({
'docstring': dict({
'description': '',
'example': '',
'full_docstring': '',
}),
'id': 'tests/data/main_package/main_module/_PrivateClass/__init__',
Expand All @@ -259,6 +265,7 @@
}),
'docstring': dict({
'description': '',
'example': '',
'full_docstring': '',
}),
'id': 'tests/data/main_package/main_module/_PrivateClass',
Expand All @@ -285,6 +292,7 @@
'constructor': None,
'docstring': dict({
'description': '',
'example': '',
'full_docstring': '',
}),
'id': 'tests/data/main_package/main_module/_PrivateClass/NestedPrivateClass',
Expand All @@ -309,6 +317,7 @@
'constructor': None,
'docstring': dict({
'description': '',
'example': '',
'full_docstring': '',
}),
'id': 'tests/data/main_package/main_module/_PrivateClass/NestedPrivateClass/NestedNestedPrivateClass',
Expand All @@ -334,6 +343,7 @@
dict({
'docstring': dict({
'description': '',
'example': '',
'full_docstring': '',
}),
'id': 'tests/data/main_package/another_path/another_module/yetAnotherClass/another_function',
Expand All @@ -354,6 +364,7 @@
dict({
'docstring': dict({
'description': '',
'example': '',
'full_docstring': '',
}),
'id': 'tests/data/main_package/main_module/ModuleClass/NestedClass/nested_class_function',
Expand All @@ -379,6 +390,7 @@

Full init description.
''',
'example': '',
'full_docstring': '''
Summary of the init description.

Expand Down Expand Up @@ -407,6 +419,7 @@

param_2: bool.
''',
'example': '',
'full_docstring': '''
Function Docstring.

Expand All @@ -433,6 +446,7 @@
dict({
'docstring': dict({
'description': '',
'example': '',
'full_docstring': '',
}),
'id': 'tests/data/main_package/main_module/_PrivateClass/NestedPrivateClass/static_nested_private_class_function',
Expand All @@ -451,6 +465,7 @@
dict({
'docstring': dict({
'description': '',
'example': '',
'full_docstring': '',
}),
'id': 'tests/data/main_package/main_module/_PrivateClass/__init__',
Expand All @@ -470,6 +485,7 @@
dict({
'docstring': dict({
'description': '',
'example': '',
'full_docstring': '',
}),
'id': 'tests/data/main_package/main_module/_PrivateClass/public_func_in_private_class',
Expand All @@ -489,6 +505,7 @@
dict({
'docstring': dict({
'description': '',
'example': '',
'full_docstring': '',
}),
'id': 'tests/data/main_package/main_module/_private_global_func',
Expand All @@ -512,6 +529,7 @@

Docstring 2.
''',
'example': '',
'full_docstring': '''
Docstring 1.

Expand Down
Loading

0 comments on commit 6665186

Please sign in to comment.