[RFC v2 17/38] scripts/get_abi.py: use an interactor for ReST output

Mauro Carvalho Chehab posted 38 patches 10 months, 3 weeks ago
[RFC v2 17/38] scripts/get_abi.py: use an interactor for ReST output
Posted by Mauro Carvalho Chehab 10 months, 3 weeks ago
Instead of printing all results line per line, use an interactor
to return each variable as a separate message.

This won't change much when using it via command line, but it
will help Sphinx integration by providing an interactor that
could be used there to handle ABI symbol by symbol.

Signed-off-by: Mauro Carvalho Chehab <mchehab+huawei@kernel.org>
---
 scripts/get_abi.py | 52 ++++++++++++++++++++++++++--------------------
 1 file changed, 29 insertions(+), 23 deletions(-)

diff --git a/scripts/get_abi.py b/scripts/get_abi.py
index 73613fb29c1c..2aec1f9dc5aa 100755
--- a/scripts/get_abi.py
+++ b/scripts/get_abi.py
@@ -349,7 +349,7 @@ class AbiParser:
                 name = os.path.join(root, entry.name)
 
                 if entry.is_dir():
-                    self.parse_abi(name)
+                    self._parse_abi(name)
                     continue
 
                 if not entry.is_file():
@@ -378,14 +378,14 @@ class AbiParser:
         if self.debug & DEBUG_DUMP_ABI_STRUCTS:
             self.log.debug(pformat(self.data))
 
-    def print_desc_txt(self, desc):
+    def desc_txt(self, desc):
         """Print description as found inside ABI files"""
 
         desc = desc.strip(" \t\n")
 
-        print(desc + "\n")
+        return desc + "\n\n"
 
-    def print_desc_rst(self, desc):
+    def desc_rst(self, desc):
         """Enrich ReST output by creating cross-references"""
 
         # Remove title markups from the description
@@ -439,9 +439,9 @@ class AbiParser:
 
             new_desc += d + "\n"
 
-        print(new_desc + "\n")
+        return new_desc + "\n\n"
 
-    def print_data(self, enable_lineno, output_in_txt, show_file=False):
+    def doc(self, enable_lineno, output_in_txt, show_file=False):
         """Print ABI at stdout"""
 
         part = None
@@ -456,9 +456,11 @@ class AbiParser:
             if not show_file and wtype == "File":
                 continue
 
+            msg = ""
+
             if enable_lineno:
                 ln = v.get("line_no", 1)
-                print(f".. LINENO {file_ref[0][0]}#{ln}\n")
+                msg += f".. LINENO {file_ref[0][0]}#{ln}\n\n"
 
             if wtype != "File":
                 cur_part = names[0]
@@ -470,9 +472,9 @@ class AbiParser:
 
                 if cur_part and cur_part != part:
                     part = cur_part
-                    print(f"{part}\n{"-" * len(part)}\n")
+                    msg += f"{part}\n{"-" * len(part)}\n\n"
 
-                print(f".. _{key}:\n")
+                msg += f".. _{key}:\n\n"
 
                 max_len = 0
                 for i in range(0, len(names)):           # pylint: disable=C0200
@@ -480,45 +482,47 @@ class AbiParser:
 
                     max_len = max(max_len, len(names[i]))
 
-                print("+-" + "-" * max_len + "-+")
+                msg += "+-" + "-" * max_len + "-+\n"
                 for name in names:
-                    print(f"| {name}" + " " * (max_len - len(name)) + " |")
-                    print("+-" + "-" * max_len + "-+")
-                print()
+                    msg += f"| {name}" + " " * (max_len - len(name)) + " |\n"
+                    msg += "+-" + "-" * max_len + "-+\n"
+                msg += "\n"
 
             for ref in file_ref:
                 if wtype == "File":
-                    print(f".. _{ref[1]}:\n")
+                    msg += f".. _{ref[1]}:\n\n"
                 else:
                     base = os.path.basename(ref[0])
-                    print(f"Defined on file :ref:`{base} <{ref[1]}>`\n")
+                    msg += f"Defined on file :ref:`{base} <{ref[1]}>`\n\n"
 
             if wtype == "File":
-                print(f"{names[0]}\n{"-" * len(names[0])}\n")
+                msg += f"{names[0]}\n{"-" * len(names[0])}\n\n"
 
             desc = v.get("description")
             if not desc and wtype != "File":
-                print(f"DESCRIPTION MISSING for {names[0]}\n")
+                msg += f"DESCRIPTION MISSING for {names[0]}\n\n"
 
             if desc:
                 if output_in_txt:
-                    self.print_desc_txt(desc)
+                    msg += self.desc_txt(desc)
                 else:
-                    self.print_desc_rst(desc)
+                    msg += self.desc_rst(desc)
 
             symbols = v.get("symbols")
             if symbols:
-                print("Has the following ABI:\n")
+                msg += "Has the following ABI:\n\n"
 
                 for w, label in symbols:
                     # Escape special chars from content
                     content = self.re_escape.sub(r"\\\1", w)
 
-                    print(f"- :ref:`{content} <{label}>`\n")
+                    msg += f"- :ref:`{content} <{label}>`\n\n"
 
             users = v.get("users")
             if users and users.strip(" \t\n"):
-                print(f"Users:\n\t{users.strip("\n").replace('\n', '\n\t')}\n")
+                msg += f"Users:\n\t{users.strip("\n").replace('\n', '\n\t')}\n\n"
+
+            yield msg
 
     def check_issues(self):
         """Warn about duplicated ABI entries"""
@@ -625,7 +629,9 @@ class AbiRest:
         parser = AbiParser(args.dir, debug=args.debug)
         parser.parse_abi()
         parser.check_issues()
-        parser.print_data(args.enable_lineno, args.raw, not args.no_file)
+
+        for msg in parser.doc(args.enable_lineno, args.raw, not args.no_file):
+            print(msg)
 
 
 class AbiValidate:
-- 
2.48.1
Re: [RFC v2 17/38] scripts/get_abi.py: use an interactor for ReST output
Posted by Akira Yokosawa 10 months, 3 weeks ago
Hi,

Mauro Carvalho Chehab wrote:

> Instead of printing all results line per line, use an interactor
> to return each variable as a separate message.
> 
> This won't change much when using it via command line, but it
> will help Sphinx integration by providing an interactor that
> could be used there to handle ABI symbol by symbol.
> 
> Signed-off-by: Mauro Carvalho Chehab <mchehab+huawei@kernel.org>
> ---
>  scripts/get_abi.py | 52 ++++++++++++++++++++++++++--------------------
>  1 file changed, 29 insertions(+), 23 deletions(-)
> 
> diff --git a/scripts/get_abi.py b/scripts/get_abi.py
> index 73613fb29c1c..2aec1f9dc5aa 100755
> --- a/scripts/get_abi.py
> +++ b/scripts/get_abi.py
[...]
> @@ -470,9 +472,9 @@ class AbiParser:
>  
>                  if cur_part and cur_part != part:
>                      part = cur_part
> -                    print(f"{part}\n{"-" * len(part)}\n")
> +                    msg += f"{part}\n{"-" * len(part)}\n\n"
>  
> -                print(f".. _{key}:\n")
> +                msg += f".. _{key}:\n\n"
>  
>                  max_len = 0
>                  for i in range(0, len(names)):           # pylint: disable=C0200
[...]

Testing under Ubuntu 22.04, where distro python3 is 3.10.12 and
distro Sphinx is 4.3.2, I get this exception by running "make htmldocs":

Exception occurred:
  File ".../linux/Documentation/sphinx/kernel_abi.py", line 48, in <module>
    from get_abi import AbiParser
  File ".../linux/scripts/get_abi.py", line 525
    msg += f"{part}\n{"-" * len(part)}\n\n"
                       ^
SyntaxError: f-string: expecting '}'

, which is introduced in the above hunk, I guess.

You can install Sphinx 8.1.3 on top of python3 3.10.12 using venv.
I get the same exception there.

Your change works fine against Ubuntu 24.04, whose distro python3 is 3.12.3.

I have not tested against python3 3.11.x.

It would be better to keep compatible with >= python 3.10.x if at all
possible.

        Thanks, Akira
Re: [RFC v2 17/38] scripts/get_abi.py: use an interactor for ReST output
Posted by Mauro Carvalho Chehab 10 months, 3 weeks ago
Em Wed, 29 Jan 2025 20:04:42 +0900
Akira Yokosawa <akiyks@gmail.com> escreveu:

> Hi,
> 
> Mauro Carvalho Chehab wrote:
> 
> > Instead of printing all results line per line, use an interactor
> > to return each variable as a separate message.
> > 
> > This won't change much when using it via command line, but it
> > will help Sphinx integration by providing an interactor that
> > could be used there to handle ABI symbol by symbol.
> > 
> > Signed-off-by: Mauro Carvalho Chehab <mchehab+huawei@kernel.org>
> > ---
> >  scripts/get_abi.py | 52 ++++++++++++++++++++++++++--------------------
> >  1 file changed, 29 insertions(+), 23 deletions(-)
> > 
> > diff --git a/scripts/get_abi.py b/scripts/get_abi.py
> > index 73613fb29c1c..2aec1f9dc5aa 100755
> > --- a/scripts/get_abi.py
> > +++ b/scripts/get_abi.py  
> [...]
> > @@ -470,9 +472,9 @@ class AbiParser:
> >  
> >                  if cur_part and cur_part != part:
> >                      part = cur_part
> > -                    print(f"{part}\n{"-" * len(part)}\n")
> > +                    msg += f"{part}\n{"-" * len(part)}\n\n"
> >  
> > -                print(f".. _{key}:\n")
> > +                msg += f".. _{key}:\n\n"
> >  
> >                  max_len = 0
> >                  for i in range(0, len(names)):           # pylint: disable=C0200  
> [...]
> 
> Testing under Ubuntu 22.04, where distro python3 is 3.10.12 and
> distro Sphinx is 4.3.2, I get this exception by running "make htmldocs":
> 
> Exception occurred:
>   File ".../linux/Documentation/sphinx/kernel_abi.py", line 48, in <module>
>     from get_abi import AbiParser
>   File ".../linux/scripts/get_abi.py", line 525
>     msg += f"{part}\n{"-" * len(part)}\n\n"
>                        ^
> SyntaxError: f-string: expecting '}'
> 
> , which is introduced in the above hunk, I guess.
> 
> You can install Sphinx 8.1.3 on top of python3 3.10.12 using venv.
> I get the same exception there.
> 
> Your change works fine against Ubuntu 24.04, whose distro python3 is 3.12.3.
> 
> I have not tested against python3 3.11.x.
> 
> It would be better to keep compatible with >= python 3.10.x if at all
> possible.

Thanks for checking it!

I was aiming to make it compatible with 3.6. Yet, it seems that f-string
support is very limited on early versions. The enclosed diff will make
it backward-compatible with Python 3.6.

Thanks,
Mauro

diff --git a/scripts/get_abi.py b/scripts/get_abi.py
index 543bed397c8c..e6e94f721fff 100755
--- a/scripts/get_abi.py
+++ b/scripts/get_abi.py
@@ -522,7 +522,7 @@ class AbiParser:
 
                 if cur_part and cur_part != part:
                     part = cur_part
-                    msg += f"{part}\n{"-" * len(part)}\n\n"
+                    msg += part + "\n"+ "-" * len(part) +"\n\n"
 
                 msg += f".. _{key}:\n\n"
 
@@ -546,7 +546,7 @@ class AbiParser:
                     msg += f"Defined on file :ref:`{base} <{ref[1]}>`\n\n"
 
             if wtype == "File":
-                msg += f"{names[0]}\n{"-" * len(names[0])}\n\n"
+                msg += names[0] +"\n" + "-" * len(names[0]) +"\n\n"
 
             desc = v.get("description")
             if not desc and wtype != "File":
@@ -570,7 +570,8 @@ class AbiParser:
 
             users = v.get("users")
             if users and users.strip(" \t\n"):
-                msg += f"Users:\n\t{users.strip("\n").replace('\n', '\n\t')}\n\n"
+                users = users.strip("\n").replace('\n', '\n\t')
+                msg += f"Users:\n\t{users}\n\n"
 
             ln = v.get("line_no", 1)
 
@@ -596,7 +597,9 @@ class AbiParser:
                 elif len(lines) == 1:
                     f.append(f"{fname}:{lines[0]}")
                 else:
-                    f.append(f"{fname} lines {", ".join(str(x) for x in lines)}")
+                    m = fname + "lines "
+                    m += ", ".join(str(x) for x in lines)
+                    f.append(m)
 
             self.log.warning("%s is defined %d times: %s", what, len(f), "; ".join(f))
 
@@ -644,10 +647,11 @@ class AbiParser:
                     if users:
                         print(f"Users:\t\t\t{users}")
 
-                    print(f"Defined on file{'s'[:len(files) ^ 1]}:\t{", ".join(files)}")
+                    print("Defined on file(s):\t" + ", ".join(files))
 
                     if desc:
-                        print(f"\n{desc.strip("\n")}\n")
+                        desc = desc.strip("\n")
+                        print(f"\n{desc}\n")
 
         if not found_keys:
             print(f"Regular expression /{expr}/ not found.")