Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add update verb #48

Draft
wants to merge 58 commits into
base: main
Choose a base branch
from
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
58 commits
Select commit Hold shift + click to select a range
4ab7154
Add update.py
3473f Mar 16, 2024
3b3a31f
[pre-commit.ci] auto fixes from pre-commit.com hooks
pre-commit-ci[bot] Mar 16, 2024
6f4dbe3
initial document parser
3473f Mar 23, 2024
9f29714
Merge branch 'feature/add-update-verb' of github.com:3473f/ros2autodo…
3473f Mar 23, 2024
b8380f4
[pre-commit.ci] auto fixes from pre-commit.com hooks
pre-commit-ci[bot] Mar 23, 2024
7f37a97
rename type to data_type
3473f Mar 23, 2024
d4dd057
add update verb to entry_points
3473f Mar 23, 2024
046c8cb
initial update documentation
3473f Mar 23, 2024
9c515f5
update parameters
3473f Mar 23, 2024
13deb27
from dict to lists
3473f Mar 23, 2024
103c3f6
create Node class
3473f Mar 24, 2024
5acacf7
get running node interface
3473f Mar 24, 2024
f10a101
[pre-commit.ci] auto fixes from pre-commit.com hooks
pre-commit-ci[bot] Mar 24, 2024
350f09f
Pump version to 1.1.0
3473f Mar 24, 2024
3b4272a
WIP
3473f Mar 24, 2024
e2a5d4e
Merge branch 'main' of github.com:3473f/ros2autodoc into feature/add-…
3473f Mar 24, 2024
594302b
[pre-commit.ci] auto fixes from pre-commit.com hooks
pre-commit-ci[bot] Mar 24, 2024
febde33
[pre-commit.ci] pre-commit autoupdate
pre-commit-ci[bot] Mar 25, 2024
718c5fc
Merge pull request #50 from 3473f/pre-commit-ci-update-config
3473f Mar 28, 2024
d1afe5d
[pre-commit.ci] pre-commit autoupdate
pre-commit-ci[bot] Apr 15, 2024
c5ce101
Merge pull request #51 from 3473f/pre-commit-ci-update-config
3473f Apr 22, 2024
0229f29
[pre-commit.ci] pre-commit autoupdate
pre-commit-ci[bot] Apr 29, 2024
76c6d09
Merge pull request #52 from 3473f/pre-commit-ci-update-config
3473f Apr 30, 2024
14bf221
add NodeInterfaceCollector class
3473f May 5, 2024
c317e24
adhere to code style
3473f May 5, 2024
b138890
pump version to 1.1.0
3473f May 5, 2024
1d25d31
untangle code
3473f May 5, 2024
fb583e4
Merge pull request #53 from 3473f/feature/untangle-code
3473f May 5, 2024
40fd50f
Add update.py
3473f Mar 16, 2024
5732259
initial document parser
3473f Mar 23, 2024
465ad84
[pre-commit.ci] auto fixes from pre-commit.com hooks
pre-commit-ci[bot] Mar 16, 2024
e197041
[pre-commit.ci] auto fixes from pre-commit.com hooks
pre-commit-ci[bot] Mar 23, 2024
98bf464
rename type to data_type
3473f Mar 23, 2024
8b5b991
add update verb to entry_points
3473f Mar 23, 2024
a82b2e7
initial update documentation
3473f Mar 23, 2024
4db2bae
update parameters
3473f Mar 23, 2024
dbe6040
from dict to lists
3473f Mar 23, 2024
b6b7ad6
create Node class
3473f Mar 24, 2024
0948a3f
get running node interface
3473f Mar 24, 2024
a3db484
[pre-commit.ci] auto fixes from pre-commit.com hooks
pre-commit-ci[bot] Mar 24, 2024
c221ca6
Pump version to 1.1.0
3473f Mar 24, 2024
d57899a
WIP
3473f Mar 24, 2024
0191fd3
[pre-commit.ci] auto fixes from pre-commit.com hooks
pre-commit-ci[bot] Mar 24, 2024
ddb7ce2
remove unused code
3473f May 5, 2024
aedad6d
fix merge conflicts
3473f May 5, 2024
cae7634
Update version to 1.2.0
3473f May 5, 2024
a80b2e9
Remove unused Node class
3473f May 5, 2024
806d315
add node_name as an argument
3473f May 5, 2024
11bf798
Implement DocParser
3473f May 5, 2024
24f7c3e
fix bug with description
3473f May 5, 2024
6378306
get nodes and interfaces
3473f May 5, 2024
1b8f8c2
[pre-commit.ci] auto fixes from pre-commit.com hooks
pre-commit-ci[bot] May 5, 2024
6118b1a
style changes
3473f May 5, 2024
51c14be
style changes
3473f May 5, 2024
4596c3b
WIP: update doc method
3473f May 5, 2024
fa65ce9
Merge branch 'feature/add-update-verb' of github.com:3473f/ros2autodo…
3473f May 5, 2024
cd9ce36
handle same params but different types and describtions
3473f May 5, 2024
0cbb9f6
remove white space
3473f May 5, 2024
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
12 changes: 6 additions & 6 deletions .pre-commit-config.yaml
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
repos:
- repo: https://github.com/pre-commit/pre-commit-hooks
rev: v4.5.0
rev: v4.6.0
hooks:
- id: check-ast
- id: check-case-conflict
Expand Down Expand Up @@ -29,7 +29,7 @@ repos:
- id: absolufy-imports
name: Make python imports absolute
- repo: https://github.com/psf/black
rev: 24.3.0
rev: 24.4.2
hooks:
- id: black
name: Python formatting (black)
Expand All @@ -45,7 +45,7 @@ repos:
name: Sorting python imports
args: [--profile, black]
- repo: https://github.com/asottile/pyupgrade
rev: v3.15.1
rev: v3.15.2
hooks:
- id: pyupgrade
name: Upgrade common mistakes
Expand All @@ -62,20 +62,20 @@ repos:
- flake8-simplify
- pep8-naming
- repo: https://github.com/astral-sh/ruff-pre-commit
rev: v0.3.3
rev: v0.4.2
hooks:
- id: ruff
name: Linting Python code (ruff)
args: [--fix, --exit-non-zero-on-fix]
- repo: https://github.com/macisamuele/language-formatters-pre-commit-hooks
rev: v2.12.0
rev: v2.13.0
hooks:
- id: pretty-format-yaml
args: [--autofix, --indent, '2']
- id: pretty-format-toml
args: [--autofix]
- repo: https://github.com/pre-commit/mirrors-mypy
rev: v1.9.0
rev: v1.10.0
hooks:
- id: mypy
name: Static typechecking (mypy)
Expand Down
8 changes: 4 additions & 4 deletions package.xml
Original file line number Diff line number Diff line change
@@ -1,9 +1,9 @@
<?xml version="1.0"?>
<package format="2">
<package format="3">
<name>ros2autodoc</name>
<version>1.0.0</version>
<version>1.2.0</version>
<description>
The ROS2 autodoc project provides a CLI command to automatically generate documentation for ROS2 nodes in markdown syntax.
The ROS2 autodoc project provides a CLI command to automatically generate ROS-Wiki-style documentation template nodes in markdown syntax.
</description>
<maintainer email="[email protected]">Mohamed Abdelaziz</maintainer>
<license>BSD-3-Clause</license>
Expand All @@ -13,4 +13,4 @@
<export>
<build_type>ament_python</build_type>
</export>
</package>
</package>
100 changes: 49 additions & 51 deletions ros2autodoc/api/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -2,26 +2,16 @@
import sys

# ROS2 node API
from ros2node.api import (
INFO_NONUNIQUE_WARNING_TEMPLATE,
get_action_server_info,
get_node_names,
get_publisher_info,
get_service_server_info,
get_subscriber_info,
)

# ROS2 param API
from ros2param.api import (
call_describe_parameters,
call_list_parameters,
get_parameter_type_string,
)
from ros2node.api import INFO_NONUNIQUE_WARNING_TEMPLATE, get_node_names

# ROS2 pkg API
from ros2pkg.api import get_executable_paths, get_package_names

from ros2autodoc.api.doc_parser import DocParser
from ros2autodoc.api.doc_writer import DocWriter
from ros2autodoc.api.node_interface_collector import NodeInterfaceCollector

TODO = "TODO: description"


def check_for_package(package_name):
Expand Down Expand Up @@ -61,42 +51,50 @@ def get_nodes(package_name):

def document_node(node, package_name, node_name, path, file_name="/README.md"):
"""Document the given node."""
writer = DocWriter(package_name, node_name)
param_names, params, description = _get_parameters(node, node_name)
if len(params) > 0:
writer.get_parameters(param_names, params, description)
subscribers = get_subscriber_info(
node=node, remote_node_name=node_name, include_hidden=False
)
if len(subscribers) > 0:
writer.get_subscribers(subscribers)
publishers = get_publisher_info(
node=node, remote_node_name=node_name, include_hidden=False
)
if len(publishers) > 0:
writer.get_publishers(publishers)
service_servers = get_service_server_info(
node=node, remote_node_name=node_name, include_hidden=False
)
if len(service_servers) > 0:
writer.get_service_servers(service_servers)
actions_servers = get_action_server_info(
node=node, remote_node_name=node_name, include_hidden=False
)
if len(actions_servers) > 0:
writer.get_action_servers(actions_servers)
interface_collector = NodeInterfaceCollector(node, node_name)
node_interface = interface_collector.get_interfaces()
writer = DocWriter(package_name, node_name, node_interface)
writer.write(path + file_name)


def _get_parameters(node, node_name):
name_to_type_map = {}
name_to_description_map = {}
parameter_names = call_list_parameters(node=node, node_name=node_name)
sorted_names = sorted(parameter_names)
resp = call_describe_parameters(
node=node, node_name=node_name, parameter_names=sorted_names
)
for descriptor in resp.descriptors:
name_to_type_map[descriptor.name] = get_parameter_type_string(descriptor.type)
name_to_description_map[descriptor.name] = descriptor.description
return sorted_names, name_to_type_map, name_to_description_map
def update_documentation(node, node_name, file_path):
"""Update the documentation for the given node."""
# Parse the document
parser = DocParser(file_path)
parser.parse()
doc_interface = parser.get_node_interface(node_name)
if not doc_interface:
print(f"Node '{node_name}' was not found in the documentation.")
return

# Get the current node interface
interface_collector = NodeInterfaceCollector(node, node_name)
node_interface = interface_collector.get_interfaces()

# Check if parameter names are the same
if sorted(doc_interface["parameters"].keys()) == sorted(
node_interface["parameters"].keys()
):
for doc_param, node_param in zip(
sorted(doc_interface["parameters"].keys()),
sorted(node_interface["parameters"].keys()),
):
# Check if the types are different
if (
doc_interface["parameters"][doc_param]["type"]
!= node_interface["parameters"][node_param]["type"]
):
node_interface["parameters"][node_param]["type"] = doc_interface[
"parameters"
][doc_param]["type"]
if (
doc_interface["parameters"][doc_param]["description"]
!= node_interface["parameters"][node_param]["description"]
and doc_interface["parameters"][doc_param]["description"] != TODO
):
node_interface["parameters"][node_param]["description"] = doc_interface[
"parameters"
][doc_param]["description"]
# If parameters are not the same
else:
pass
204 changes: 204 additions & 0 deletions ros2autodoc/api/doc_parser.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,204 @@
import os
import re


class DocParser:
def __init__(self, path):
self.path = path
self.text_to_keep = []
self.nodes = {}

def parse(self):
"""Parse the documentation."""
if not os.path.exists(self.path):
print("File not found.")
return

with open(self.path) as file:
start_parsing = False
parse_params = False
parse_subs = False
parse_pubs = False
parse_srvs = False
parse_actions = False

for line in file:
if line.strip().startswith("## Nodes"):
self.text_to_keep.append(line)
start_parsing = True
continue

if not start_parsing:
self.text_to_keep.append(line)
continue

if line.strip().startswith("### Parameters"):
parse_params = True
parse_subs = False
parse_pubs = False
parse_srvs = False
parse_actions = False
continue
elif line.strip().startswith("### Subscribers"):
parse_params = False
parse_subs = True
parse_pubs = False
parse_srvs = False
parse_actions = False
continue
elif line.strip().startswith("### Publishers"):
parse_params = False
parse_subs = False
parse_pubs = True
parse_srvs = False
parse_actions = False
continue
elif line.strip().startswith("### Services"):
parse_params = False
parse_subs = False
parse_pubs = False
parse_srvs = True
parse_actions = False
continue
elif line.strip().startswith("### Actions"):
parse_params = False
parse_subs = False
parse_pubs = False
parse_srvs = False
parse_actions = True
continue
elif line.strip().startswith("### "):
# Rest flags
parse_params = False
parse_subs = False
parse_pubs = False
parse_srvs = False
parse_actions = False

# Create new node
curr_node_name = line.strip()[4:]
self.nodes[curr_node_name] = {
"parameters": {},
"subscribers": {},
"publishers": {},
"services": {},
"actions": {},
}
continue

if parse_params:
if line.startswith("- **"):
param_name = line.split("**`")[1].split("`**")[0]
if param_name:
self.nodes[curr_node_name]["parameters"][param_name] = {
"type": "",
"description": "",
}
match = re.search(r"\((.*?)\)", line)
if match:
data_type = match.group(1)
else:
data_type = ""
self.nodes[curr_node_name]["parameters"][param_name][
"type"
] = data_type
elif line.startswith(" ") and line.strip():
self.nodes[curr_node_name]["parameters"][param_name][
"description"
] = line.strip()
continue

elif parse_subs:
if line.startswith("- **"):
sub_name = line.split("**`")[1].split("`**")[0]
if sub_name:
self.nodes[curr_node_name]["subscribers"][sub_name] = {
"type": "",
"description": "",
}
match = re.search(r"\((.*?)\)", line)
if match:
data_type = match.group(1)
else:
data_type = ""
self.nodes[curr_node_name]["subscribers"][sub_name][
"type"
] = data_type
elif line.startswith(" ") and sub_name:
self.nodes[curr_node_name]["subscribers"][sub_name][
"description"
] = line.strip()
continue

elif parse_pubs:
if line.startswith("- **"):
pub_name = line.split("**`")[1].split("`**")[0]
if pub_name:
self.nodes[curr_node_name]["publishers"][pub_name] = {
"type": "",
"description": "",
}
match = re.search(r"\((.*?)\)", line)
if match:
data_type = match.group(1)
else:
data_type = ""
self.nodes[curr_node_name]["publishers"][pub_name][
"type"
] = data_type
elif line.startswith(" ") and pub_name:
self.nodes[curr_node_name]["publishers"][pub_name][
"description"
] = line.strip()
continue

elif parse_srvs:
if line.startswith("- **"):
srvs_name = line.split("**`")[1].split("`**")[0]
if srvs_name:
self.nodes[curr_node_name]["services"][srvs_name] = {
"type": "",
"description": "",
}
match = re.search(r"\((.*?)\)", line)
if match:
data_type = match.group(1)
else:
data_type = ""
self.nodes[curr_node_name]["services"][srvs_name][
"type"
] = data_type
elif line.startswith(" ") and srvs_name:
self.nodes[curr_node_name]["services"][srvs_name][
"description"
] = line.strip()
continue

elif parse_actions:
if line.startswith("- **"):
action_name = line.split("**`")[1].split("`**")[0]
if action_name:
self.nodes[curr_node_name]["actions"][action_name] = {
"type": "",
"description": "",
}
match = re.search(r"\((.*?)\)", line)
if match:
data_type = match.group(1)
else:
data_type = ""
self.nodes[curr_node_name]["actions"][action_name][
"type"
] = data_type
elif line.startswith(" ") and action_name:
self.nodes[curr_node_name]["actions"][action_name][
"description"
] = line.strip()

def get_nodes(self):
return self.nodes

def get_node_interface(self, node_name):
if node_name in self.nodes:
return self.nodes[node_name]
return None
Loading
Loading