Skip to content

Commit

Permalink
update docs
Browse files Browse the repository at this point in the history
  • Loading branch information
goFrendiAsgard committed Dec 9, 2023
1 parent 07ecf20 commit 82252ec
Show file tree
Hide file tree
Showing 10 changed files with 282 additions and 183 deletions.
51 changes: 48 additions & 3 deletions docs/concepts/task-group.md
Original file line number Diff line number Diff line change
Expand Up @@ -49,7 +49,7 @@ print(group.get_tasks())
```

```
[<Task name=first-task>, <Task name=second-task>]
[<Task "group first-task">, <Task "group second-task">]
```


Expand Down Expand Up @@ -98,7 +98,7 @@ print(group.get_children())
```

```
[<Group name=sub-group-1>, <Group name=sub-group-2>]
[<Group "group sub-group-1">, <Group "group sub-group-2">]
```


Expand All @@ -125,6 +125,51 @@ my-system
```


### `Group.get_description`

Retrieves group description.

__Returns:__

`str`: Description of the group.

__Examples:__

```python
from zrb import Group
group = Group(name='group', description='description of the group')
print(group.get_description())
```

```
description of the group
```


### `Group.get_parent`

Retrieves parent of the Group.

__Returns:__

`Optional[Group]`: Parent of the group.

__Examples:__

```python
from zrb import Group
system_group = Group(name='my system')
system_log_group = Group(name='log', parent=system_group)
print(system_group.get_parent())
print(system_log_group.get_parent())
```

```
None
<Group "my-system">
```


### `Group.get_tasks`

Get direct Tasks under this Task Group.
Expand All @@ -144,7 +189,7 @@ print(group.get_tasks())
```

```
[<Task name=first-task>, <Task name=second-task>]
[<Task "group first-task">, <Task "group second-task">]
```


Expand Down
124 changes: 2 additions & 122 deletions helper/doc.py
Original file line number Diff line number Diff line change
@@ -1,9 +1,9 @@
import inspect
from zrb.helper.docstring import get_markdown_from_docstring
import re


def inject_doc(markdown_file_name: str, cls):
docstring_markdown = docstring_to_markdown(cls)
docstring_markdown = get_markdown_from_docstring(cls)
with open(markdown_file_name, 'r') as file:
original_content = file.read()
pattern = r'<!--start-doc-->.*?<!--end-doc-->'
Expand All @@ -17,123 +17,3 @@ def inject_doc(markdown_file_name: str, cls):
)
with open(markdown_file_name, 'w') as file:
file.write(new_content)


def docstring_to_markdown(cls) -> str:
"""
Convert Google Style docstrings of a class and its methods to Markdown.
Args:
cls (class): The class whose docstrings are to be converted.
Returns:
str: The converted Markdown text.
"""
markdown = f"## `{cls.__name__}`\n\n"
cls_doc = get_doc(cls)
markdown += parse_docstring(cls_doc) + '\n'
for method_name, _ in inspect.getmembers(cls, predicate=inspect.isfunction): # noqa
if method_name.startswith('__'):
# Don't parse private or protected function
continue
markdown += f"\n### `{cls.__name__}.{method_name}`\n\n"
method_doc = get_method_doc(cls, method_name)
markdown += parse_docstring(method_doc) + '\n'
return markdown


def get_method_doc(cls, method_name: str) -> str:
if not hasattr(cls, method_name):
return None
method = getattr(cls, method_name)
if not method.__doc__ and hasattr(cls, '__bases__'):
for parent in cls.__bases__:
parent_method_doc = get_method_doc(parent, method_name)
if parent_method_doc:
return parent_method_doc
return method.__doc__


def get_doc(cls) -> str:
if not cls.__doc__ and hasattr(cls, '__bases__'):
for parent in cls.__bases__:
parent_doc = get_doc(parent)
if parent_doc:
return parent_doc
return cls.__doc__


def parse_docstring(docstring: str) -> str:
if not docstring:
return 'No documentation available.\n'
# Split the docstring into lines
lines = docstring.strip().split('\n')
line_length = len(lines)
# Process each line
markdown_lines = []
section = ''
is_previous_code = False
has_unclosed_backtick = False
for line_index, line in enumerate(lines):
line = line.strip()
is_code = line.startswith('>>> ')
is_last_line = line_index == line_length - 1
is_new_section = False
# Add code backticks
if is_code and not is_previous_code:
markdown_lines.append('```python')
has_unclosed_backtick = True
elif section == 'examples' and is_previous_code and not is_code:
markdown_lines.append('```')
markdown_lines.append('')
markdown_lines.append('```')
has_unclosed_backtick = True
elif is_previous_code and not is_code:
markdown_lines.append('````')
markdown_lines.append('')
has_unclosed_backtick = False
# Handle lines
if is_code:
markdown_lines.append(line[4:])
elif line.startswith('Attributes:'):
markdown_lines.append('__Attributes:__\n')
section = 'attributes'
is_new_section = True
elif line.startswith('Args:'):
markdown_lines.append('__Arguments:__\n')
section = 'args'
is_new_section = True
elif line.startswith('Returns:'):
markdown_lines.append('__Returns:__\n')
section = 'returns'
is_new_section = True
elif line.startswith('Examples:'):
markdown_lines.append('__Examples:__\n')
section = 'examples'
is_new_section = True
elif line == '```':
markdown_lines.append(line)
elif section == 'args' or section == 'attributes':
named_param_match = re.match(r'^(\w+)\s+\((.+)\):(.+)$', line)
if named_param_match:
param_name, param_type, param_desc = named_param_match.groups()
markdown_lines.append(
f'- `{param_name}` (`{param_type}`): {param_desc.strip()}'
)
else:
markdown_lines.append(line)
elif section == 'returns':
return_match = re.match(r'^(.+):(.+)$', line)
if return_match:
param_type, param_desc = return_match.groups()
markdown_lines.append(f'`{param_type}`: {param_desc.strip()}')
else:
markdown_lines.append(line)
else:
markdown_lines.append(line)
is_previous_code = is_code
if (is_new_section or is_last_line) and has_unclosed_backtick:
markdown_lines.append('```')
markdown_lines.append('')
has_unclosed_backtick = False
return '\n'.join(markdown_lines)
7 changes: 4 additions & 3 deletions src/zrb/action/runner.py
Original file line number Diff line number Diff line change
Expand Up @@ -59,10 +59,11 @@ def __create_cli_subcommand(
task_group = task._group
while task_group is not None:
group = self.__register_sub_command(task_group, subcommand)
if task_group._parent is None:
parent_group = task_group.get_parent()
if parent_group is None:
return group
subcommand = group
task_group = task_group._parent
task_group = parent_group
return subcommand

def __register_sub_command(
Expand All @@ -82,7 +83,7 @@ def __get_cli_group(self, task_group: TaskGroup) -> click.Group:
if task_group_id in self.__registered_groups:
return self.__registered_groups[task_group_id]
group_cli_name = task_group.get_cli_name()
group_description = task_group._description
group_description = task_group.get_description()
group = click.Group(name=group_cli_name, help=group_description)
self.__registered_groups[task_group_id] = group
return group
Expand Down
122 changes: 122 additions & 0 deletions src/zrb/helper/docstring.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,122 @@
import inspect
import re


def get_markdown_from_docstring(cls) -> str:
"""
Convert Google Style docstrings of a class and its methods to Markdown.
Args:
cls (class): The class whose docstrings are to be converted.
Returns:
str: The converted Markdown text.
"""
markdown = f"## `{cls.__name__}`\n\n"
cls_doc = get_doc(cls)
markdown += parse_docstring(cls_doc) + '\n'
for method_name, _ in inspect.getmembers(cls, predicate=inspect.isfunction): # noqa
if method_name.startswith('__'):
# Don't parse private or protected function
continue
markdown += f"\n### `{cls.__name__}.{method_name}`\n\n"
method_doc = get_method_doc(cls, method_name)
markdown += parse_docstring(method_doc) + '\n'
return markdown


def get_method_doc(cls, method_name: str) -> str:
if not hasattr(cls, method_name):
return None
method = getattr(cls, method_name)
if not method.__doc__ and hasattr(cls, '__bases__'):
for parent in cls.__bases__:
parent_method_doc = get_method_doc(parent, method_name)
if parent_method_doc:
return parent_method_doc
return method.__doc__


def get_doc(cls) -> str:
if not cls.__doc__ and hasattr(cls, '__bases__'):
for parent in cls.__bases__:
parent_doc = get_doc(parent)
if parent_doc:
return parent_doc
return cls.__doc__


def parse_docstring(docstring: str) -> str:
if not docstring:
return 'No documentation available.\n'
# Split the docstring into lines
lines = docstring.strip().split('\n')
line_length = len(lines)
# Process each line
markdown_lines = []
section = ''
is_previous_code = False
has_unclosed_backtick = False
for line_index, line in enumerate(lines):
line = line.strip()
is_code = line.startswith('>>> ')
is_last_line = line_index == line_length - 1
is_new_section = False
# Add code backticks
if is_code and not is_previous_code:
markdown_lines.append('```python')
has_unclosed_backtick = True
elif section == 'examples' and is_previous_code and not is_code:
markdown_lines.append('```')
markdown_lines.append('')
markdown_lines.append('```')
has_unclosed_backtick = True
elif is_previous_code and not is_code:
markdown_lines.append('````')
markdown_lines.append('')
has_unclosed_backtick = False
# Handle lines
if is_code:
markdown_lines.append(line[4:])
elif line.startswith('Attributes:'):
markdown_lines.append('__Attributes:__\n')
section = 'attributes'
is_new_section = True
elif line.startswith('Args:'):
markdown_lines.append('__Arguments:__\n')
section = 'args'
is_new_section = True
elif line.startswith('Returns:'):
markdown_lines.append('__Returns:__\n')
section = 'returns'
is_new_section = True
elif line.startswith('Examples:'):
markdown_lines.append('__Examples:__\n')
section = 'examples'
is_new_section = True
elif line == '```':
markdown_lines.append(line)
elif section == 'args' or section == 'attributes':
named_param_match = re.match(r'^(\w+)\s+\((.+)\):(.+)$', line)
if named_param_match:
param_name, param_type, param_desc = named_param_match.groups()
markdown_lines.append(
f'- `{param_name}` (`{param_type}`): {param_desc.strip()}'
)
else:
markdown_lines.append(line)
elif section == 'returns':
return_match = re.match(r'^(.+):(.+)$', line)
if return_match:
param_type, param_desc = return_match.groups()
markdown_lines.append(f'`{param_type}`: {param_desc.strip()}')
else:
markdown_lines.append(line)
else:
markdown_lines.append(line)
is_previous_code = is_code
if (is_new_section or is_last_line) and has_unclosed_backtick:
markdown_lines.append('```')
markdown_lines.append('')
has_unclosed_backtick = False
return '\n'.join(markdown_lines)
Loading

0 comments on commit 82252ec

Please sign in to comment.