* [PATCH net-next 0/7] tools: ynl: cli: improve the help and doc
@ 2026-01-09 21:17 Jakub Kicinski
2026-01-09 21:17 ` [PATCH net-next 1/7] tools: ynl: cli: introduce formatting for attr names in --list-attrs Jakub Kicinski
` (6 more replies)
0 siblings, 7 replies; 11+ messages in thread
From: Jakub Kicinski @ 2026-01-09 21:17 UTC (permalink / raw)
To: davem
Cc: netdev, edumazet, pabeni, andrew+netdev, horms, donald.hunter,
gal, Jakub Kicinski
I had some time on the plane to LPC, so here are improvements
to the --help and --list-attrs handling of YNL CLI which seem
in order given growing use of YNL as a real CLI tool.
Jakub Kicinski (7):
tools: ynl: cli: introduce formatting for attr names in --list-attrs
tools: ynl: cli: wrap the doc text if it's long
tools: ynl: cli: improve --help
tools: ynl: cli: add --doc as alias to --list-attrs
tools: ynl: cli: factor out --list-attrs / --doc handling
tools: ynl: cli: extract the event/notify handling in --list-attrs
tools: ynl: cli: print reply in combined format if possible
tools/net/ynl/pyynl/cli.py | 210 ++++++++++++++++++++++++++-----------
1 file changed, 147 insertions(+), 63 deletions(-)
--
2.52.0
^ permalink raw reply [flat|nested] 11+ messages in thread
* [PATCH net-next 1/7] tools: ynl: cli: introduce formatting for attr names in --list-attrs
2026-01-09 21:17 [PATCH net-next 0/7] tools: ynl: cli: improve the help and doc Jakub Kicinski
@ 2026-01-09 21:17 ` Jakub Kicinski
2026-01-09 21:17 ` [PATCH net-next 2/7] tools: ynl: cli: wrap the doc text if it's long Jakub Kicinski
` (5 subsequent siblings)
6 siblings, 0 replies; 11+ messages in thread
From: Jakub Kicinski @ 2026-01-09 21:17 UTC (permalink / raw)
To: davem
Cc: netdev, edumazet, pabeni, andrew+netdev, horms, donald.hunter,
gal, Jakub Kicinski
It's a little hard to make sense of the output of --list-attrs,
it looks like a wall of text. Sprinkle a little bit of formatting -
make op and attr names bold, and Enum: / Flags: keywords italics.
Signed-off-by: Jakub Kicinski <kuba@kernel.org>
---
tools/net/ynl/pyynl/cli.py | 30 +++++++++++++++++++++++++++---
1 file changed, 27 insertions(+), 3 deletions(-)
diff --git a/tools/net/ynl/pyynl/cli.py b/tools/net/ynl/pyynl/cli.py
index 5fee45e48bbf..aa50d42e35ac 100755
--- a/tools/net/ynl/pyynl/cli.py
+++ b/tools/net/ynl/pyynl/cli.py
@@ -20,6 +20,29 @@ from lib import YnlFamily, Netlink, NlError, SpecFamily, SpecException, YnlExcep
SYS_SCHEMA_DIR='/usr/share/ynl'
RELATIVE_SCHEMA_DIR='../../../../Documentation/netlink'
+# pylint: disable=too-few-public-methods,too-many-locals
+class Colors:
+ """ANSI color and font modifier codes"""
+ RESET = '\033[0m'
+
+ BOLD = '\033[1m'
+ ITALICS = '\033[3m'
+ UNDERLINE = '\033[4m'
+ INVERT = '\033[7m'
+
+
+def color(text, modifiers):
+ """Add color to text if output is a TTY
+
+ Returns:
+ Colored text if stdout is a TTY, otherwise plain text
+ """
+ if sys.stdout.isatty():
+ # Join the colors if they are a list, if it's a string this a noop
+ modifiers = "".join(modifiers)
+ return f"{modifiers}{text}{Colors.RESET}"
+ return text
+
def schema_dir():
"""
Return the effective schema directory, preferring in-tree before
@@ -60,7 +83,7 @@ RELATIVE_SCHEMA_DIR='../../../../Documentation/netlink'
for attr_name in attr_names:
if attr_name in attr_set.attrs:
attr = attr_set.attrs[attr_name]
- attr_info = f'{prefix}- {attr_name}: {attr.type}'
+ attr_info = f'{prefix}- {color(attr_name, Colors.BOLD)}: {attr.type}'
if 'enum' in attr.yaml:
enum_name = attr.yaml['enum']
attr_info += f" (enum: {enum_name})"
@@ -68,7 +91,8 @@ RELATIVE_SCHEMA_DIR='../../../../Documentation/netlink'
if enum_name in ynl.consts:
const = ynl.consts[enum_name]
enum_values = list(const.entries.keys())
- attr_info += f"\n{prefix} {const.type.capitalize()}: {', '.join(enum_values)}"
+ type_fmted = color(const.type.capitalize(), Colors.ITALICS)
+ attr_info += f"\n{prefix} {type_fmted}: {', '.join(enum_values)}"
# Show nested attributes reference and recursively display them
nested_set_name = None
@@ -226,7 +250,7 @@ RELATIVE_SCHEMA_DIR='../../../../Documentation/netlink'
print(f'Operation {args.list_attrs} not found')
sys.exit(1)
- print(f'Operation: {op.name}')
+ print(f'Operation: {color(op.name, Colors.BOLD)}')
print(op.yaml['doc'])
for mode in ['do', 'dump', 'event']:
--
2.52.0
^ permalink raw reply related [flat|nested] 11+ messages in thread
* [PATCH net-next 2/7] tools: ynl: cli: wrap the doc text if it's long
2026-01-09 21:17 [PATCH net-next 0/7] tools: ynl: cli: improve the help and doc Jakub Kicinski
2026-01-09 21:17 ` [PATCH net-next 1/7] tools: ynl: cli: introduce formatting for attr names in --list-attrs Jakub Kicinski
@ 2026-01-09 21:17 ` Jakub Kicinski
2026-01-10 19:04 ` Stanislav Fomichev
2026-01-09 21:17 ` [PATCH net-next 3/7] tools: ynl: cli: improve --help Jakub Kicinski
` (4 subsequent siblings)
6 siblings, 1 reply; 11+ messages in thread
From: Jakub Kicinski @ 2026-01-09 21:17 UTC (permalink / raw)
To: davem
Cc: netdev, edumazet, pabeni, andrew+netdev, horms, donald.hunter,
gal, Jakub Kicinski
We already use textwrap when printing "doc" section about an attribute,
but only to indent the text. Switch to using fill() to split and indent
all the lines. While at it indent the text by 2 more spaces, so that it
doesn't align with the name of the attribute.
Before (I'm drawing a "box" at ~60 cols here, in an attempt for clarity):
| - irq-suspend-timeout: uint |
| The timeout, in nanoseconds, of how long to suspend irq|
|processing, if event polling finds events |
After:
| - irq-suspend-timeout: uint |
| The timeout, in nanoseconds, of how long to suspend |
| irq processing, if event polling finds events |
Signed-off-by: Jakub Kicinski <kuba@kernel.org>
---
tools/net/ynl/pyynl/cli.py | 10 +++++++++-
1 file changed, 9 insertions(+), 1 deletion(-)
diff --git a/tools/net/ynl/pyynl/cli.py b/tools/net/ynl/pyynl/cli.py
index aa50d42e35ac..e5e71ee4e133 100755
--- a/tools/net/ynl/pyynl/cli.py
+++ b/tools/net/ynl/pyynl/cli.py
@@ -10,6 +10,7 @@ import json
import os
import pathlib
import pprint
+import shutil
import sys
import textwrap
@@ -101,7 +102,14 @@ RELATIVE_SCHEMA_DIR='../../../../Documentation/netlink'
attr_info += f" -> {nested_set_name}"
if attr.yaml.get('doc'):
- doc_text = textwrap.indent(attr.yaml['doc'], prefix + ' ')
+ doc_prefix = prefix + ' ' * 4
+ if sys.stdout.isatty():
+ term_width = shutil.get_terminal_size().columns
+ else:
+ term_width = 80
+ doc_text = textwrap.fill(attr.yaml['doc'], width=term_width,
+ initial_indent=doc_prefix,
+ subsequent_indent=doc_prefix)
attr_info += f"\n{doc_text}"
print(attr_info)
--
2.52.0
^ permalink raw reply related [flat|nested] 11+ messages in thread
* [PATCH net-next 3/7] tools: ynl: cli: improve --help
2026-01-09 21:17 [PATCH net-next 0/7] tools: ynl: cli: improve the help and doc Jakub Kicinski
2026-01-09 21:17 ` [PATCH net-next 1/7] tools: ynl: cli: introduce formatting for attr names in --list-attrs Jakub Kicinski
2026-01-09 21:17 ` [PATCH net-next 2/7] tools: ynl: cli: wrap the doc text if it's long Jakub Kicinski
@ 2026-01-09 21:17 ` Jakub Kicinski
2026-01-09 21:17 ` [PATCH net-next 4/7] tools: ynl: cli: add --doc as alias to --list-attrs Jakub Kicinski
` (3 subsequent siblings)
6 siblings, 0 replies; 11+ messages in thread
From: Jakub Kicinski @ 2026-01-09 21:17 UTC (permalink / raw)
To: davem
Cc: netdev, edumazet, pabeni, andrew+netdev, horms, donald.hunter,
gal, Jakub Kicinski
Improve the clarity of --help. Reorder, provide some grouping and
add help messages to most of the options.
No functional changes intended.
Signed-off-by: Jakub Kicinski <kuba@kernel.org>
---
tools/net/ynl/pyynl/cli.py | 107 ++++++++++++++++++++++++-------------
1 file changed, 69 insertions(+), 38 deletions(-)
diff --git a/tools/net/ynl/pyynl/cli.py b/tools/net/ynl/pyynl/cli.py
index e5e71ee4e133..6ddb3dac08bc 100755
--- a/tools/net/ynl/pyynl/cli.py
+++ b/tools/net/ynl/pyynl/cli.py
@@ -154,47 +154,78 @@ RELATIVE_SCHEMA_DIR='../../../../Documentation/netlink'
"""
parser = argparse.ArgumentParser(description=description,
- epilog=epilog)
- spec_group = parser.add_mutually_exclusive_group(required=True)
- spec_group.add_argument('--family', dest='family', type=str,
- help='name of the netlink FAMILY')
- spec_group.add_argument('--list-families', action='store_true',
- help='list all netlink families supported by YNL (has spec)')
- spec_group.add_argument('--spec', dest='spec', type=str,
- help='choose the family by SPEC file path')
+ epilog=epilog, add_help=False)
- parser.add_argument('--schema', dest='schema', type=str)
- parser.add_argument('--no-schema', action='store_true')
- parser.add_argument('--json', dest='json_text', type=str)
+ gen_group = parser.add_argument_group('General options')
+ gen_group.add_argument('-h', '--help', action='help',
+ help='show this help message and exit')
- group = parser.add_mutually_exclusive_group()
- group.add_argument('--do', dest='do', metavar='DO-OPERATION', type=str)
- group.add_argument('--multi', dest='multi', nargs=2, action='append',
- metavar=('DO-OPERATION', 'JSON_TEXT'), type=str)
- group.add_argument('--dump', dest='dump', metavar='DUMP-OPERATION', type=str)
- group.add_argument('--list-ops', action='store_true')
- group.add_argument('--list-msgs', action='store_true')
- group.add_argument('--list-attrs', dest='list_attrs', metavar='OPERATION', type=str,
- help='List attributes for an operation')
- group.add_argument('--validate', action='store_true')
+ spec_group = parser.add_argument_group('Netlink family selection')
+ spec_sel = spec_group.add_mutually_exclusive_group(required=True)
+ spec_sel.add_argument('--list-families', action='store_true',
+ help=('list Netlink families supported by YNL '
+ '(which have a spec available in the standard '
+ 'system path)'))
+ spec_sel.add_argument('--family', dest='family', type=str,
+ help='name of the Netlink FAMILY to use')
+ spec_sel.add_argument('--spec', dest='spec', type=str,
+ help='full file path to the YAML spec file')
+
+ ops_group = parser.add_argument_group('Operations')
+ ops = ops_group.add_mutually_exclusive_group()
+ ops.add_argument('--do', dest='do', metavar='DO-OPERATION', type=str)
+ ops.add_argument('--dump', dest='dump', metavar='DUMP-OPERATION', type=str)
+ ops.add_argument('--multi', dest='multi', nargs=2, action='append',
+ metavar=('DO-OPERATION', 'JSON_TEXT'), type=str,
+ help="Multi-message operation sequence (for nftables)")
+ ops.add_argument('--list-ops', action='store_true',
+ help="List available --do and --dump operations")
+ ops.add_argument('--list-msgs', action='store_true',
+ help="List all messages of the family (incl. notifications)")
+ ops.add_argument('--list-attrs', dest='list_attrs', metavar='MSG',
+ type=str, help='List attributes for a message / operation')
+ ops.add_argument('--validate', action='store_true',
+ help="Validate the spec against schema and exit")
+
+ io_group = parser.add_argument_group('Input / Output')
+ io_group.add_argument('--json', dest='json_text', type=str,
+ help=('Specify attributes of the message to send '
+ 'to the kernel in JSON format. Can be left out '
+ 'if the message is expected to be empty.'))
+ io_group.add_argument('--output-json', action='store_true',
+ help='Format output as JSON')
+
+ ntf_group = parser.add_argument_group('Notifications')
+ ntf_group.add_argument('--subscribe', dest='ntf', type=str)
+ ntf_group.add_argument('--duration', dest='duration', type=int,
+ help='when subscribed, watch for DURATION seconds')
+ ntf_group.add_argument('--sleep', dest='duration', type=int,
+ help='alias for duration')
+
+ nlflags = parser.add_argument_group('Netlink message flags (NLM_F_*)',
+ ('Extra flags to set in nlmsg_flags of '
+ 'the request, used mostly by older '
+ 'Classic Netlink families.'))
+ nlflags.add_argument('--replace', dest='flags', action='append_const',
+ const=Netlink.NLM_F_REPLACE)
+ nlflags.add_argument('--excl', dest='flags', action='append_const',
+ const=Netlink.NLM_F_EXCL)
+ nlflags.add_argument('--create', dest='flags', action='append_const',
+ const=Netlink.NLM_F_CREATE)
+ nlflags.add_argument('--append', dest='flags', action='append_const',
+ const=Netlink.NLM_F_APPEND)
+
+ schema_group = parser.add_argument_group('Development options')
+ schema_group.add_argument('--schema', dest='schema', type=str,
+ help="JSON schema to validate the spec")
+ schema_group.add_argument('--no-schema', action='store_true')
+
+ dbg_group = parser.add_argument_group('Debug options')
+ dbg_group.add_argument('--dbg-small-recv', default=0, const=4000,
+ action='store', nargs='?', type=int, metavar='INT',
+ help="Length of buffers used for recv()")
+ dbg_group.add_argument('--process-unknown', action=argparse.BooleanOptionalAction)
- parser.add_argument('--duration', dest='duration', type=int,
- help='when subscribed, watch for DURATION seconds')
- parser.add_argument('--sleep', dest='duration', type=int,
- help='alias for duration')
- parser.add_argument('--subscribe', dest='ntf', type=str)
- parser.add_argument('--replace', dest='flags', action='append_const',
- const=Netlink.NLM_F_REPLACE)
- parser.add_argument('--excl', dest='flags', action='append_const',
- const=Netlink.NLM_F_EXCL)
- parser.add_argument('--create', dest='flags', action='append_const',
- const=Netlink.NLM_F_CREATE)
- parser.add_argument('--append', dest='flags', action='append_const',
- const=Netlink.NLM_F_APPEND)
- parser.add_argument('--process-unknown', action=argparse.BooleanOptionalAction)
- parser.add_argument('--output-json', action='store_true')
- parser.add_argument('--dbg-small-recv', default=0, const=4000,
- action='store', nargs='?', type=int)
args = parser.parse_args()
def output(msg):
--
2.52.0
^ permalink raw reply related [flat|nested] 11+ messages in thread
* [PATCH net-next 4/7] tools: ynl: cli: add --doc as alias to --list-attrs
2026-01-09 21:17 [PATCH net-next 0/7] tools: ynl: cli: improve the help and doc Jakub Kicinski
` (2 preceding siblings ...)
2026-01-09 21:17 ` [PATCH net-next 3/7] tools: ynl: cli: improve --help Jakub Kicinski
@ 2026-01-09 21:17 ` Jakub Kicinski
2026-01-09 21:17 ` [PATCH net-next 5/7] tools: ynl: cli: factor out --list-attrs / --doc handling Jakub Kicinski
` (2 subsequent siblings)
6 siblings, 0 replies; 11+ messages in thread
From: Jakub Kicinski @ 2026-01-09 21:17 UTC (permalink / raw)
To: davem
Cc: netdev, edumazet, pabeni, andrew+netdev, horms, donald.hunter,
gal, Jakub Kicinski
--list-attrs also provides information about the operation itself.
So --doc seems more appropriate. Add an alias.
Signed-off-by: Jakub Kicinski <kuba@kernel.org>
---
tools/net/ynl/pyynl/cli.py | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/tools/net/ynl/pyynl/cli.py b/tools/net/ynl/pyynl/cli.py
index 6ddb3dac08bc..88e9fbe4ac5b 100755
--- a/tools/net/ynl/pyynl/cli.py
+++ b/tools/net/ynl/pyynl/cli.py
@@ -182,7 +182,7 @@ RELATIVE_SCHEMA_DIR='../../../../Documentation/netlink'
help="List available --do and --dump operations")
ops.add_argument('--list-msgs', action='store_true',
help="List all messages of the family (incl. notifications)")
- ops.add_argument('--list-attrs', dest='list_attrs', metavar='MSG',
+ ops.add_argument('--list-attrs', '--doc', dest='list_attrs', metavar='MSG',
type=str, help='List attributes for a message / operation')
ops.add_argument('--validate', action='store_true',
help="Validate the spec against schema and exit")
--
2.52.0
^ permalink raw reply related [flat|nested] 11+ messages in thread
* [PATCH net-next 5/7] tools: ynl: cli: factor out --list-attrs / --doc handling
2026-01-09 21:17 [PATCH net-next 0/7] tools: ynl: cli: improve the help and doc Jakub Kicinski
` (3 preceding siblings ...)
2026-01-09 21:17 ` [PATCH net-next 4/7] tools: ynl: cli: add --doc as alias to --list-attrs Jakub Kicinski
@ 2026-01-09 21:17 ` Jakub Kicinski
2026-01-09 21:17 ` [PATCH net-next 6/7] tools: ynl: cli: extract the event/notify handling in --list-attrs Jakub Kicinski
2026-01-09 21:17 ` [PATCH net-next 7/7] tools: ynl: cli: print reply in combined format if possible Jakub Kicinski
6 siblings, 0 replies; 11+ messages in thread
From: Jakub Kicinski @ 2026-01-09 21:17 UTC (permalink / raw)
To: davem
Cc: netdev, edumazet, pabeni, andrew+netdev, horms, donald.hunter,
gal, Jakub Kicinski
We'll soon add more code to the --doc handling. Factor it out
to avoid making main() too long.
Signed-off-by: Jakub Kicinski <kuba@kernel.org>
---
tools/net/ynl/pyynl/cli.py | 35 ++++++++++++++++++++---------------
1 file changed, 20 insertions(+), 15 deletions(-)
diff --git a/tools/net/ynl/pyynl/cli.py b/tools/net/ynl/pyynl/cli.py
index 88e9fbe4ac5b..e0deb50a4ee7 100755
--- a/tools/net/ynl/pyynl/cli.py
+++ b/tools/net/ynl/pyynl/cli.py
@@ -140,6 +140,25 @@ RELATIVE_SCHEMA_DIR='../../../../Documentation/netlink'
print_attr_list(ynl, mode_spec['attributes'], attr_set)
+def do_doc(ynl, op):
+ """Handle --list-attrs $op, print the attr information to stdout"""
+ print(f'Operation: {color(op.name, Colors.BOLD)}')
+ print(op.yaml['doc'])
+
+ for mode in ['do', 'dump', 'event']:
+ if mode in op.yaml:
+ print_mode_attrs(ynl, mode, op.yaml[mode], op.attr_set, True)
+
+ if 'notify' in op.yaml:
+ mode_spec = op.yaml['notify']
+ ref_spec = ynl.msgs.get(mode_spec).yaml.get('do')
+ if ref_spec:
+ print_mode_attrs(ynl, 'notify', ref_spec, op.attr_set, False)
+
+ if 'mcgrp' in op.yaml:
+ print(f"\nMulticast group: {op.yaml['mcgrp']}")
+
+
# pylint: disable=too-many-locals,too-many-branches,too-many-statements
def main():
"""YNL cli tool"""
@@ -289,21 +308,7 @@ RELATIVE_SCHEMA_DIR='../../../../Documentation/netlink'
print(f'Operation {args.list_attrs} not found')
sys.exit(1)
- print(f'Operation: {color(op.name, Colors.BOLD)}')
- print(op.yaml['doc'])
-
- for mode in ['do', 'dump', 'event']:
- if mode in op.yaml:
- print_mode_attrs(ynl, mode, op.yaml[mode], op.attr_set, True)
-
- if 'notify' in op.yaml:
- mode_spec = op.yaml['notify']
- ref_spec = ynl.msgs.get(mode_spec).yaml.get('do')
- if ref_spec:
- print_mode_attrs(ynl, 'notify', ref_spec, op.attr_set, False)
-
- if 'mcgrp' in op.yaml:
- print(f"\nMulticast group: {op.yaml['mcgrp']}")
+ do_doc(ynl, op)
try:
if args.do:
--
2.52.0
^ permalink raw reply related [flat|nested] 11+ messages in thread
* [PATCH net-next 6/7] tools: ynl: cli: extract the event/notify handling in --list-attrs
2026-01-09 21:17 [PATCH net-next 0/7] tools: ynl: cli: improve the help and doc Jakub Kicinski
` (4 preceding siblings ...)
2026-01-09 21:17 ` [PATCH net-next 5/7] tools: ynl: cli: factor out --list-attrs / --doc handling Jakub Kicinski
@ 2026-01-09 21:17 ` Jakub Kicinski
2026-01-09 21:17 ` [PATCH net-next 7/7] tools: ynl: cli: print reply in combined format if possible Jakub Kicinski
6 siblings, 0 replies; 11+ messages in thread
From: Jakub Kicinski @ 2026-01-09 21:17 UTC (permalink / raw)
To: davem
Cc: netdev, edumazet, pabeni, andrew+netdev, horms, donald.hunter,
gal, Jakub Kicinski
Event and notify handling is quite different from do / dump
handling. Forcing it into print_mode_attrs() doesn't really
buy us anything as events and notifications do not have requests.
Call print_attr_list() directly. Apart form subjective code
clarity this also removes the word "reply" from the output:
Before:
Event reply attributes:
Now:
Event attributes:
Signed-off-by: Jakub Kicinski <kuba@kernel.org>
---
tools/net/ynl/pyynl/cli.py | 21 ++++++++++++---------
1 file changed, 12 insertions(+), 9 deletions(-)
diff --git a/tools/net/ynl/pyynl/cli.py b/tools/net/ynl/pyynl/cli.py
index e0deb50a4ee7..dc3438345714 100755
--- a/tools/net/ynl/pyynl/cli.py
+++ b/tools/net/ynl/pyynl/cli.py
@@ -123,11 +123,11 @@ RELATIVE_SCHEMA_DIR='../../../../Documentation/netlink'
print_attr_list(ynl, nested_names, nested_set, indent + 4)
-def print_mode_attrs(ynl, mode, mode_spec, attr_set, print_request=True):
+def print_mode_attrs(ynl, mode, mode_spec, attr_set):
"""Print a given mode (do/dump/event/notify)."""
mode_title = mode.capitalize()
- if print_request and 'request' in mode_spec and 'attributes' in mode_spec['request']:
+ if 'request' in mode_spec and 'attributes' in mode_spec['request']:
print(f'\n{mode_title} request attributes:')
print_attr_list(ynl, mode_spec['request']['attributes'], attr_set)
@@ -135,25 +135,28 @@ RELATIVE_SCHEMA_DIR='../../../../Documentation/netlink'
print(f'\n{mode_title} reply attributes:')
print_attr_list(ynl, mode_spec['reply']['attributes'], attr_set)
- if 'attributes' in mode_spec:
- print(f'\n{mode_title} attributes:')
- print_attr_list(ynl, mode_spec['attributes'], attr_set)
-
def do_doc(ynl, op):
"""Handle --list-attrs $op, print the attr information to stdout"""
print(f'Operation: {color(op.name, Colors.BOLD)}')
print(op.yaml['doc'])
- for mode in ['do', 'dump', 'event']:
+ for mode in ['do', 'dump']:
if mode in op.yaml:
- print_mode_attrs(ynl, mode, op.yaml[mode], op.attr_set, True)
+ print_mode_attrs(ynl, mode, op.yaml[mode], op.attr_set)
+
+ if 'attributes' in op.yaml.get('event', {}):
+ print('\nEvent attributes:')
+ print_attr_list(ynl, op.yaml['event']['attributes'], op.attr_set)
if 'notify' in op.yaml:
mode_spec = op.yaml['notify']
ref_spec = ynl.msgs.get(mode_spec).yaml.get('do')
+ if not ref_spec:
+ ref_spec = ynl.msgs.get(mode_spec).yaml.get('dump')
if ref_spec:
- print_mode_attrs(ynl, 'notify', ref_spec, op.attr_set, False)
+ print('\nNotification attributes:')
+ print_attr_list(ynl, ref_spec['reply']['attributes'], op.attr_set)
if 'mcgrp' in op.yaml:
print(f"\nMulticast group: {op.yaml['mcgrp']}")
--
2.52.0
^ permalink raw reply related [flat|nested] 11+ messages in thread
* [PATCH net-next 7/7] tools: ynl: cli: print reply in combined format if possible
2026-01-09 21:17 [PATCH net-next 0/7] tools: ynl: cli: improve the help and doc Jakub Kicinski
` (5 preceding siblings ...)
2026-01-09 21:17 ` [PATCH net-next 6/7] tools: ynl: cli: extract the event/notify handling in --list-attrs Jakub Kicinski
@ 2026-01-09 21:17 ` Jakub Kicinski
6 siblings, 0 replies; 11+ messages in thread
From: Jakub Kicinski @ 2026-01-09 21:17 UTC (permalink / raw)
To: davem
Cc: netdev, edumazet, pabeni, andrew+netdev, horms, donald.hunter,
gal, Jakub Kicinski
As pointed out during review of the --list-attrs support the GET
ops very often return the same attrs from do and dump. Make the
output more readable by combining the reply information, from:
Do request attributes:
- ifindex: u32
netdev ifindex
Do reply attributes:
- ifindex: u32
netdev ifindex
[ .. other attrs .. ]
Dump reply attributes:
- ifindex: u32
netdev ifindex
[ .. other attrs .. ]
To, after:
Do request attributes:
- ifindex: u32
netdev ifindex
Do and Dump reply attributes:
- ifindex: u32
netdev ifindex
[ .. other attrs .. ]
Signed-off-by: Jakub Kicinski <kuba@kernel.org>
---
tools/net/ynl/pyynl/cli.py | 21 +++++++++++++++++----
1 file changed, 17 insertions(+), 4 deletions(-)
diff --git a/tools/net/ynl/pyynl/cli.py b/tools/net/ynl/pyynl/cli.py
index dc3438345714..2a9ffe1f0fc2 100755
--- a/tools/net/ynl/pyynl/cli.py
+++ b/tools/net/ynl/pyynl/cli.py
@@ -123,7 +123,7 @@ RELATIVE_SCHEMA_DIR='../../../../Documentation/netlink'
print_attr_list(ynl, nested_names, nested_set, indent + 4)
-def print_mode_attrs(ynl, mode, mode_spec, attr_set):
+def print_mode_attrs(ynl, mode, mode_spec, attr_set, consistent_dd_reply=None):
"""Print a given mode (do/dump/event/notify)."""
mode_title = mode.capitalize()
@@ -132,8 +132,15 @@ RELATIVE_SCHEMA_DIR='../../../../Documentation/netlink'
print_attr_list(ynl, mode_spec['request']['attributes'], attr_set)
if 'reply' in mode_spec and 'attributes' in mode_spec['reply']:
- print(f'\n{mode_title} reply attributes:')
- print_attr_list(ynl, mode_spec['reply']['attributes'], attr_set)
+ if consistent_dd_reply and mode == "do":
+ title = None # Dump handling will print in combined format
+ elif consistent_dd_reply and mode == "dump":
+ title = 'Do and Dump'
+ else:
+ title = f'{mode_title}'
+ if title:
+ print(f'\n{title} reply attributes:')
+ print_attr_list(ynl, mode_spec['reply']['attributes'], attr_set)
def do_doc(ynl, op):
@@ -141,9 +148,15 @@ RELATIVE_SCHEMA_DIR='../../../../Documentation/netlink'
print(f'Operation: {color(op.name, Colors.BOLD)}')
print(op.yaml['doc'])
+ consistent_dd_reply = False
+ if 'do' in op.yaml and 'dump' in op.yaml and 'reply' in op.yaml['do'] and \
+ op.yaml['do']['reply'] == op.yaml['dump'].get('reply'):
+ consistent_dd_reply = True
+
for mode in ['do', 'dump']:
if mode in op.yaml:
- print_mode_attrs(ynl, mode, op.yaml[mode], op.attr_set)
+ print_mode_attrs(ynl, mode, op.yaml[mode], op.attr_set,
+ consistent_dd_reply=consistent_dd_reply)
if 'attributes' in op.yaml.get('event', {}):
print('\nEvent attributes:')
--
2.52.0
^ permalink raw reply related [flat|nested] 11+ messages in thread
* Re: [PATCH net-next 2/7] tools: ynl: cli: wrap the doc text if it's long
2026-01-09 21:17 ` [PATCH net-next 2/7] tools: ynl: cli: wrap the doc text if it's long Jakub Kicinski
@ 2026-01-10 19:04 ` Stanislav Fomichev
2026-01-10 19:07 ` Jakub Kicinski
0 siblings, 1 reply; 11+ messages in thread
From: Stanislav Fomichev @ 2026-01-10 19:04 UTC (permalink / raw)
To: Jakub Kicinski
Cc: davem, netdev, edumazet, pabeni, andrew+netdev, horms,
donald.hunter, gal
On 01/09, Jakub Kicinski wrote:
> We already use textwrap when printing "doc" section about an attribute,
> but only to indent the text. Switch to using fill() to split and indent
> all the lines. While at it indent the text by 2 more spaces, so that it
> doesn't align with the name of the attribute.
>
> Before (I'm drawing a "box" at ~60 cols here, in an attempt for clarity):
>
> | - irq-suspend-timeout: uint |
> | The timeout, in nanoseconds, of how long to suspend irq|
> |processing, if event polling finds events |
>
> After:
>
> | - irq-suspend-timeout: uint |
> | The timeout, in nanoseconds, of how long to suspend |
> | irq processing, if event polling finds events |
>
> Signed-off-by: Jakub Kicinski <kuba@kernel.org>
> ---
> tools/net/ynl/pyynl/cli.py | 10 +++++++++-
> 1 file changed, 9 insertions(+), 1 deletion(-)
>
> diff --git a/tools/net/ynl/pyynl/cli.py b/tools/net/ynl/pyynl/cli.py
> index aa50d42e35ac..e5e71ee4e133 100755
> --- a/tools/net/ynl/pyynl/cli.py
> +++ b/tools/net/ynl/pyynl/cli.py
> @@ -10,6 +10,7 @@ import json
> import os
> import pathlib
> import pprint
> +import shutil
> import sys
> import textwrap
>
> @@ -101,7 +102,14 @@ RELATIVE_SCHEMA_DIR='../../../../Documentation/netlink'
> attr_info += f" -> {nested_set_name}"
>
> if attr.yaml.get('doc'):
> - doc_text = textwrap.indent(attr.yaml['doc'], prefix + ' ')
> + doc_prefix = prefix + ' ' * 4
> + if sys.stdout.isatty():
> + term_width = shutil.get_terminal_size().columns
> + else:
> + term_width = 80
> + doc_text = textwrap.fill(attr.yaml['doc'], width=term_width,
> + initial_indent=doc_prefix,
> + subsequent_indent=doc_prefix)
Any specific reason you wrap to 80 for !isatty?
^ permalink raw reply [flat|nested] 11+ messages in thread
* Re: [PATCH net-next 2/7] tools: ynl: cli: wrap the doc text if it's long
2026-01-10 19:04 ` Stanislav Fomichev
@ 2026-01-10 19:07 ` Jakub Kicinski
2026-01-10 23:26 ` Jakub Kicinski
0 siblings, 1 reply; 11+ messages in thread
From: Jakub Kicinski @ 2026-01-10 19:07 UTC (permalink / raw)
To: Stanislav Fomichev
Cc: davem, netdev, edumazet, pabeni, andrew+netdev, horms,
donald.hunter, gal
On Sat, 10 Jan 2026 11:04:08 -0800 Stanislav Fomichev wrote:
> > @@ -101,7 +102,14 @@ RELATIVE_SCHEMA_DIR='../../../../Documentation/netlink'
> > attr_info += f" -> {nested_set_name}"
> >
> > if attr.yaml.get('doc'):
> > - doc_text = textwrap.indent(attr.yaml['doc'], prefix + ' ')
> > + doc_prefix = prefix + ' ' * 4
> > + if sys.stdout.isatty():
> > + term_width = shutil.get_terminal_size().columns
> > + else:
> > + term_width = 80
> > + doc_text = textwrap.fill(attr.yaml['doc'], width=term_width,
> > + initial_indent=doc_prefix,
> > + subsequent_indent=doc_prefix)
>
> Any specific reason you wrap to 80 for !isatty?
not really, just the default width of a classic terminal
the textwrap library defaults to 70 which is another option tho
I doubt there's a strong reason behind that value either..
^ permalink raw reply [flat|nested] 11+ messages in thread
* Re: [PATCH net-next 2/7] tools: ynl: cli: wrap the doc text if it's long
2026-01-10 19:07 ` Jakub Kicinski
@ 2026-01-10 23:26 ` Jakub Kicinski
0 siblings, 0 replies; 11+ messages in thread
From: Jakub Kicinski @ 2026-01-10 23:26 UTC (permalink / raw)
To: Stanislav Fomichev
Cc: davem, netdev, edumazet, pabeni, andrew+netdev, horms,
donald.hunter, gal
On Sat, 10 Jan 2026 11:07:57 -0800 Jakub Kicinski wrote:
> > > + if sys.stdout.isatty():
> > > + term_width = shutil.get_terminal_size().columns
> > > + else:
> > > + term_width = 80
> > > + doc_text = textwrap.fill(attr.yaml['doc'], width=term_width,
> > > + initial_indent=doc_prefix,
> > > + subsequent_indent=doc_prefix)
> >
> > Any specific reason you wrap to 80 for !isatty?
>
> not really, just the default width of a classic terminal
> the textwrap library defaults to 70 which is another option tho
> I doubt there's a strong reason behind that value either..
Experimenting with this a little bit more it looks like shutil does the
right thing and returns 80 if not sys.stdout.isatty() Let me respin v2
with the condition removed.
--
pw-bot: cr
^ permalink raw reply [flat|nested] 11+ messages in thread
end of thread, other threads:[~2026-01-10 23:26 UTC | newest]
Thread overview: 11+ messages (download: mbox.gz follow: Atom feed
-- links below jump to the message on this page --
2026-01-09 21:17 [PATCH net-next 0/7] tools: ynl: cli: improve the help and doc Jakub Kicinski
2026-01-09 21:17 ` [PATCH net-next 1/7] tools: ynl: cli: introduce formatting for attr names in --list-attrs Jakub Kicinski
2026-01-09 21:17 ` [PATCH net-next 2/7] tools: ynl: cli: wrap the doc text if it's long Jakub Kicinski
2026-01-10 19:04 ` Stanislav Fomichev
2026-01-10 19:07 ` Jakub Kicinski
2026-01-10 23:26 ` Jakub Kicinski
2026-01-09 21:17 ` [PATCH net-next 3/7] tools: ynl: cli: improve --help Jakub Kicinski
2026-01-09 21:17 ` [PATCH net-next 4/7] tools: ynl: cli: add --doc as alias to --list-attrs Jakub Kicinski
2026-01-09 21:17 ` [PATCH net-next 5/7] tools: ynl: cli: factor out --list-attrs / --doc handling Jakub Kicinski
2026-01-09 21:17 ` [PATCH net-next 6/7] tools: ynl: cli: extract the event/notify handling in --list-attrs Jakub Kicinski
2026-01-09 21:17 ` [PATCH net-next 7/7] tools: ynl: cli: print reply in combined format if possible Jakub Kicinski
This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox