diff mbox series

[net-next,v1,2/3] doc: netlink: Add hyperlinks to generated Netlink docs

Message ID 20240326201311.13089-3-donald.hunter@gmail.com (mailing list archive)
State Superseded
Delegated to: Netdev Maintainers
Headers show
Series doc: netlink: Add hyperlinks to generated docs | expand

Checks

Context Check Description
netdev/series_format success Posting correctly formatted
netdev/tree_selection success Clearly marked for net-next
netdev/ynl success Generated files up to date; no warnings/errors; no diff in generated;
netdev/fixes_present success Fixes tag not required for -next series
netdev/header_inline success No static functions without inline keyword in header files
netdev/build_32bit success Errors and warnings before: 8 this patch: 8
netdev/build_tools success Errors and warnings before: 0 this patch: 0
netdev/cc_maintainers success CCed 6 of 6 maintainers
netdev/build_clang success Errors and warnings before: 8 this patch: 8
netdev/verify_signedoff success Signed-off-by tag matches author and committer
netdev/deprecated_api success None detected
netdev/check_selftest success No net selftest shell script
netdev/verify_fixes success No Fixes tag
netdev/build_allmodconfig_warn success Errors and warnings before: 8 this patch: 8
netdev/checkpatch success total: 0 errors, 0 warnings, 0 checks, 118 lines checked
netdev/build_clang_rust success No Rust files in patch. Skipping build
netdev/kdoc success Errors and warnings before: 0 this patch: 0
netdev/source_inline success Was 0 now: 0
netdev/contest success net-next-2024-03-27--15-00 (tests: 952)

Commit Message

Donald Hunter March 26, 2024, 8:13 p.m. UTC
Update ynl-gen-rst to generate hyperlinks to definitions, attribute
sets and sub-messages from all the places that reference them.

Note that there is a single label namespace for all of the kernel docs.
Hyperlinks within a single netlink doc need to be qualified by the
family name to avoid collisions.

The label format is 'family-type-name' which gives, for example,
'rt-link-attribute-set-link-attrs' as the link id.

Signed-off-by: Donald Hunter <donald.hunter@gmail.com>
---
 tools/net/ynl/ynl-gen-rst.py | 44 +++++++++++++++++++++++++++---------
 1 file changed, 33 insertions(+), 11 deletions(-)

Comments

Breno Leitao March 28, 2024, 3 p.m. UTC | #1
Hi Donald,

On Tue, Mar 26, 2024 at 08:13:10PM +0000, Donald Hunter wrote:
> Update ynl-gen-rst to generate hyperlinks to definitions, attribute
> sets and sub-messages from all the places that reference them.
> 
> Note that there is a single label namespace for all of the kernel docs.
> Hyperlinks within a single netlink doc need to be qualified by the
> family name to avoid collisions.
> 
> The label format is 'family-type-name' which gives, for example,
> 'rt-link-attribute-set-link-attrs' as the link id.
> 
> Signed-off-by: Donald Hunter <donald.hunter@gmail.com>
> ---
>  tools/net/ynl/ynl-gen-rst.py | 44 +++++++++++++++++++++++++++---------
>  1 file changed, 33 insertions(+), 11 deletions(-)
> 
> diff --git a/tools/net/ynl/ynl-gen-rst.py b/tools/net/ynl/ynl-gen-rst.py
> index 5825a8b3bfb4..4be931c9bdbf 100755
> --- a/tools/net/ynl/ynl-gen-rst.py
> +++ b/tools/net/ynl/ynl-gen-rst.py
> @@ -82,9 +82,9 @@ def rst_subsubsection(title: str) -> str:
>      return f"{title}\n" + "~" * len(title)
>  
>  
> -def rst_section(title: str) -> str:
> +def rst_section(prefix: str, title: str) -> str:
>      """Add a section to the document"""
> -    return f"\n{title}\n" + "=" * len(title)
> +    return f".. _{family}-{prefix}-{title}:\n\n{title}\n" + "=" * len(title)

Where is 'family' variable set? Is this a global variable somewhere?
Donald Hunter March 28, 2024, 3:54 p.m. UTC | #2
On Thu, 28 Mar 2024 at 15:00, Breno Leitao <leitao@debian.org> wrote:
>
> Hi Donald,
>
> On Tue, Mar 26, 2024 at 08:13:10PM +0000, Donald Hunter wrote:
> > Update ynl-gen-rst to generate hyperlinks to definitions, attribute
> > sets and sub-messages from all the places that reference them.
> >
> > Note that there is a single label namespace for all of the kernel docs.
> > Hyperlinks within a single netlink doc need to be qualified by the
> > family name to avoid collisions.
> >
> > The label format is 'family-type-name' which gives, for example,
> > 'rt-link-attribute-set-link-attrs' as the link id.
> >
> > Signed-off-by: Donald Hunter <donald.hunter@gmail.com>
> > ---
> >  tools/net/ynl/ynl-gen-rst.py | 44 +++++++++++++++++++++++++++---------
> >  1 file changed, 33 insertions(+), 11 deletions(-)
> >
> > diff --git a/tools/net/ynl/ynl-gen-rst.py b/tools/net/ynl/ynl-gen-rst.py
> > index 5825a8b3bfb4..4be931c9bdbf 100755
> > --- a/tools/net/ynl/ynl-gen-rst.py
> > +++ b/tools/net/ynl/ynl-gen-rst.py
> > @@ -82,9 +82,9 @@ def rst_subsubsection(title: str) -> str:
> >      return f"{title}\n" + "~" * len(title)
> >
> >
> > -def rst_section(title: str) -> str:
> > +def rst_section(prefix: str, title: str) -> str:
> >      """Add a section to the document"""
> > -    return f"\n{title}\n" + "=" * len(title)
> > +    return f".. _{family}-{prefix}-{title}:\n\n{title}\n" + "=" * len(title)
>
> Where is 'family' variable set? Is this a global variable somewhere?

Yes, here in parse_yaml(). I realise it's a bit of a hack but would
like to clean this up as part of switching to using ynl/lib/nlspec.py
for reading the specs, in a separate patchset.

-    title = f"Family ``{obj['name']}`` netlink specification"
+    # Save the family for use in ref labels
+    global family
+    family = obj['name']
+
+    title = f"Family ``{family}`` netlink specification"
Breno Leitao March 28, 2024, 11:38 p.m. UTC | #3
On Thu, Mar 28, 2024 at 03:54:09PM +0000, Donald Hunter wrote:

> > > -def rst_section(title: str) -> str:
> > > +def rst_section(prefix: str, title: str) -> str:
> > >      """Add a section to the document"""
> > > -    return f"\n{title}\n" + "=" * len(title)
> > > +    return f".. _{family}-{prefix}-{title}:\n\n{title}\n" + "=" * len(title)
> >
> > Where is 'family' variable set? Is this a global variable somewhere?
> 
> Yes, here in parse_yaml(). I realise it's a bit of a hack but would
> like to clean this up as part of switching to using ynl/lib/nlspec.py
> for reading the specs, in a separate patchset.

Thanks. Is it worth adding a hack that would be removed later, other
than going straight to the final solution?

> -    title = f"Family ``{obj['name']}`` netlink specification"
> +    # Save the family for use in ref labels
> +    global family

Is it hard to pass this variable by without having to use a global
variable?
Jakub Kicinski March 29, 2024, 1:38 a.m. UTC | #4
On Tue, 26 Mar 2024 20:13:10 +0000 Donald Hunter wrote:
>      return headroom(level) + "[" + ", ".join(inline(i) for i in list_) + "]"
>  
> +def rst_ref(prefix: str, name: str) -> str:

I think python coding guidelines call for 2 empty lines between
functions? There's another place where this is violated in the patch.

FWIW I also feel like the using the global directly is a bit too hacky.
Dunno how much work it'd be to pass it in, but if it's a lot let's at
least define it at the start of the file and always have "global family"
before the use?
Donald Hunter March 29, 2024, 1:05 p.m. UTC | #5
Jakub Kicinski <kuba@kernel.org> writes:

> On Tue, 26 Mar 2024 20:13:10 +0000 Donald Hunter wrote:
>>      return headroom(level) + "[" + ", ".join(inline(i) for i in list_) + "]"
>>  
>> +def rst_ref(prefix: str, name: str) -> str:
>
> I think python coding guidelines call for 2 empty lines between
> functions? There's another place where this is violated in the patch.

Good catch. I'll fix in v2.

> FWIW I also feel like the using the global directly is a bit too hacky.
> Dunno how much work it'd be to pass it in, but if it's a lot let's at
> least define it at the start of the file and always have "global family"
> before the use?

I will just bite the bullet and pass a parameter in everywhere for v2.
diff mbox series

Patch

diff --git a/tools/net/ynl/ynl-gen-rst.py b/tools/net/ynl/ynl-gen-rst.py
index 5825a8b3bfb4..4be931c9bdbf 100755
--- a/tools/net/ynl/ynl-gen-rst.py
+++ b/tools/net/ynl/ynl-gen-rst.py
@@ -82,9 +82,9 @@  def rst_subsubsection(title: str) -> str:
     return f"{title}\n" + "~" * len(title)
 
 
-def rst_section(title: str) -> str:
+def rst_section(prefix: str, title: str) -> str:
     """Add a section to the document"""
-    return f"\n{title}\n" + "=" * len(title)
+    return f".. _{family}-{prefix}-{title}:\n\n{title}\n" + "=" * len(title)
 
 
 def rst_subtitle(title: str) -> str:
@@ -101,6 +101,16 @@  def rst_list_inline(list_: List[str], level: int = 0) -> str:
     """Format a list using inlines"""
     return headroom(level) + "[" + ", ".join(inline(i) for i in list_) + "]"
 
+def rst_ref(prefix: str, name: str) -> str:
+    """Add a hyperlink to the document"""
+    mappings = {'enum': 'definition',
+                'fixed-header': 'definition',
+                'nested-attributes': 'attribute-set',
+                'struct': 'definition'}
+    if prefix in mappings:
+        prefix = mappings[prefix]
+    return f":ref:`{family}-{prefix}-{name}`"
+
 
 def rst_header() -> str:
     """The headers for all the auto generated RST files"""
@@ -162,17 +172,21 @@  def parse_do_attributes(attrs: Dict[str, Any], level: int = 0) -> str:
 def parse_operations(operations: List[Dict[str, Any]]) -> str:
     """Parse operations block"""
     preprocessed = ["name", "doc", "title", "do", "dump"]
+    linkable = ["fixed-header", "attribute-set"]
     lines = []
 
     for operation in operations:
-        lines.append(rst_section(operation["name"]))
+        lines.append(rst_section('operation', operation["name"]))
         lines.append(rst_paragraph(sanitize(operation["doc"])) + "\n")
 
         for key in operation.keys():
             if key in preprocessed:
                 # Skip the special fields
                 continue
-            lines.append(rst_fields(key, operation[key], 0))
+            value = operation[key]
+            if key in linkable:
+                value = rst_ref(key, value)
+            lines.append(rst_fields(key, value, 0))
 
         if "do" in operation:
             lines.append(rst_paragraph(":do:", 0))
@@ -219,7 +233,7 @@  def parse_definitions(defs: Dict[str, Any]) -> str:
     lines = []
 
     for definition in defs:
-        lines.append(rst_section(definition["name"]))
+        lines.append(rst_section('definition', definition["name"]))
         for k in definition.keys():
             if k in preprocessed + ignored:
                 continue
@@ -240,11 +254,12 @@  def parse_definitions(defs: Dict[str, Any]) -> str:
 def parse_attr_sets(entries: List[Dict[str, Any]]) -> str:
     """Parse attribute from attribute-set"""
     preprocessed = ["name", "type"]
+    linkable = ["enum", "nested-attributes", "struct", "sub-message"]
     ignored = ["checks"]
     lines = []
 
     for entry in entries:
-        lines.append(rst_section(entry["name"]))
+        lines.append(rst_section('attribute-set', entry["name"]))
         for attr in entry["attributes"]:
             type_ = attr.get("type")
             attr_line = attr["name"]
@@ -257,7 +272,11 @@  def parse_attr_sets(entries: List[Dict[str, Any]]) -> str:
             for k in attr.keys():
                 if k in preprocessed + ignored:
                     continue
-                lines.append(rst_fields(k, sanitize(attr[k]), 0))
+                if k in linkable:
+                    value = rst_ref(k, attr[k])
+                else:
+                    value = sanitize(attr[k])
+                lines.append(rst_fields(k, value, 0))
             lines.append("\n")
 
     return "\n".join(lines)
@@ -268,14 +287,14 @@  def parse_sub_messages(entries: List[Dict[str, Any]]) -> str:
     lines = []
 
     for entry in entries:
-        lines.append(rst_section(entry["name"]))
+        lines.append(rst_section('sub-message', entry["name"]))
         for fmt in entry["formats"]:
             value = fmt["value"]
 
             lines.append(rst_bullet(bold(value)))
             for attr in ['fixed-header', 'attribute-set']:
                 if attr in fmt:
-                    lines.append(rst_fields(attr, fmt[attr], 1))
+                    lines.append(rst_fields(attr, rst_ref(attr, fmt[attr]), 1))
             lines.append("\n")
 
     return "\n".join(lines)
@@ -289,7 +308,11 @@  def parse_yaml(obj: Dict[str, Any]) -> str:
 
     lines.append(rst_header())
 
-    title = f"Family ``{obj['name']}`` netlink specification"
+    # Save the family for use in ref labels
+    global family
+    family = obj['name']
+
+    title = f"Family ``{family}`` netlink specification"
     lines.append(rst_title(title))
     lines.append(rst_paragraph(".. contents:: :depth: 3\n"))
 
@@ -398,7 +421,6 @@  def generate_main_index_rst(output: str) -> None:
     logging.debug("Writing an index file at %s", output)
     write_to_rstfile("".join(lines), output)
 
-
 def main() -> None:
     """Main function that reads the YAML files and generates the RST files"""