[v1,1/2] usertools/cpu_layout: update coding style

Message ID e51d00fea7c535404dda4d63e483639b13c48b2d.1723634354.git.anatoly.burakov@intel.com (mailing list archive)
State Superseded
Delegated to: Thomas Monjalon
Headers
Series [v1,1/2] usertools/cpu_layout: update coding style |

Checks

Context Check Description
ci/checkpatch success coding style OK

Commit Message

Burakov, Anatoly Aug. 14, 2024, 11:19 a.m. UTC
Update coding style:

- make it PEP-484 compliant
- address all flake8, mypy etc. warnings
- use f-strings in place of old-style string interpolation
- refactor printing to make the code more readable

Signed-off-by: Anatoly Burakov <anatoly.burakov@intel.com>
---
 usertools/cpu_layout.py | 162 ++++++++++++++++++++++++++--------------
 1 file changed, 104 insertions(+), 58 deletions(-)
  

Comments

Robin Jarry Aug. 19, 2024, 9:26 a.m. UTC | #1
Anatoly Burakov, Aug 14, 2024 at 13:19:
> Update coding style:
>
> - make it PEP-484 compliant
> - address all flake8, mypy etc. warnings
> - use f-strings in place of old-style string interpolation
> - refactor printing to make the code more readable
>
> Signed-off-by: Anatoly Burakov <anatoly.burakov@intel.com>
> ---

Hi Anatoly,

thanks for the patch. Did you format the code using black/ruff? I'd like 
to start keeping python code formatting consistent across user tools.

>  usertools/cpu_layout.py | 162 ++++++++++++++++++++++++++--------------
>  1 file changed, 104 insertions(+), 58 deletions(-)
>
> diff --git a/usertools/cpu_layout.py b/usertools/cpu_layout.py
> index 891b9238fa..843b29a134 100755
> --- a/usertools/cpu_layout.py
> +++ b/usertools/cpu_layout.py
> @@ -3,62 +3,108 @@
>  # Copyright(c) 2010-2014 Intel Corporation
>  # Copyright(c) 2017 Cavium, Inc. All rights reserved.
>  
> -sockets = []
> -cores = []
> -core_map = {}
> -base_path = "/sys/devices/system/cpu"
> -fd = open("{}/kernel_max".format(base_path))
> -max_cpus = int(fd.read())
> -fd.close()
> -for cpu in range(max_cpus + 1):
> -    try:
> -        fd = open("{}/cpu{}/topology/core_id".format(base_path, cpu))
> -    except IOError:
> -        continue
> -    core = int(fd.read())
> -    fd.close()
> -    fd = open("{}/cpu{}/topology/physical_package_id".format(base_path, cpu))
> -    socket = int(fd.read())
> -    fd.close()
> -    if core not in cores:
> -        cores.append(core)
> -    if socket not in sockets:
> -        sockets.append(socket)
> -    key = (socket, core)
> -    if key not in core_map:
> -        core_map[key] = []
> -    core_map[key].append(cpu)
> -
> -print(format("=" * (47 + len(base_path))))
> -print("Core and Socket Information (as reported by '{}')".format(base_path))
> -print("{}\n".format("=" * (47 + len(base_path))))
> -print("cores = ", cores)
> -print("sockets = ", sockets)
> -print("")
> -
> -max_processor_len = len(str(len(cores) * len(sockets) * 2 - 1))
> -max_thread_count = len(list(core_map.values())[0])
> -max_core_map_len = (max_processor_len * max_thread_count)  \
> -                      + len(", ") * (max_thread_count - 1) \
> -                      + len('[]') + len('Socket ')
> -max_core_id_len = len(str(max(cores)))
> -
> -output = " ".ljust(max_core_id_len + len('Core '))
> -for s in sockets:
> -    output += " Socket %s" % str(s).ljust(max_core_map_len - len('Socket '))
> -print(output)
> -
> -output = " ".ljust(max_core_id_len + len('Core '))
> -for s in sockets:
> -    output += " --------".ljust(max_core_map_len)
> -    output += " "
> -print(output)
> -
> -for c in cores:
> -    output = "Core %s" % str(c).ljust(max_core_id_len)
> -    for s in sockets:
> -        if (s, c) in core_map:
> -            output += " " + str(core_map[(s, c)]).ljust(max_core_map_len)
> +from typing import List, Set, Dict, Tuple
> +
> +
> +def _range_expand(rstr: str) -> List[int]:

I don't see any reason to prefix the symbols with an underscore in this 
script. It will not be used as an importable library.

> +    """Expand a range string into a list of integers."""
> +    # 0,1-3 => [0, 1-3]
> +    ranges = rstr.split(",")
> +    valset: List[int] = []
> +    for r in ranges:
> +        # 1-3 => [1, 2, 3]
> +        if "-" in r:
> +            start, end = r.split("-")
> +            valset.extend(range(int(start), int(end) + 1))
>          else:
> -            output += " " * (max_core_map_len + 1)
> -    print(output)
> +            valset.append(int(r))
> +    return valset
> +
> +
> +def _read_sysfs(path: str) -> str:
> +    with open(path) as fd:
> +        return fd.read().strip()
> +
> +
> +def _print_row(row: Tuple[str, ...], col_widths: List[int]) -> None:
> +    first, *rest = row
> +    w_first, *w_rest = col_widths
> +    first_end = " " * 4
> +    rest_end = " " * 10
> +
> +    print(first.ljust(w_first), end=first_end)
> +    for cell, width in zip(rest, w_rest):
> +        print(cell.rjust(width), end=rest_end)
> +    print()
> +
> +
> +def _print_section(heading: str) -> None:
> +    sep = "=" * len(heading)
> +    print(sep)
> +    print(heading)
> +    print(sep)
> +    print()
> +
> +
> +def _main() -> None:
> +    sockets_s: Set[int] = set()
> +    cores_s: Set[int] = set()
> +    core_map: Dict[Tuple[int, int], List[int]] = {}
> +    base_path = "/sys/devices/system/cpu"
> +
> +    cpus = _range_expand(_read_sysfs(f"{base_path}/online"))
> +
> +    for cpu in cpus:
> +        lcore_base = f"{base_path}/cpu{cpu}"
> +        core = int(_read_sysfs(f"{lcore_base}/topology/core_id"))
> +        socket = int(_read_sysfs(f"{lcore_base}/topology/physical_package_id"))
> +
> +        cores_s.add(core)
> +        sockets_s.add(socket)
> +        key = (socket, core)
> +        core_map.setdefault(key, [])
> +        core_map[key].append(cpu)
> +
> +    cores = sorted(cores_s)
> +    sockets = sorted(sockets_s)
> +
> +    _print_section("Core and Socket Information "
> +                   f"(as reported by '{base_path}')")
> +
> +    print("cores = ", cores)
> +    print("sockets = ", sockets)
> +    print()
> +
> +    # Core, [Socket, Socket, ...]
> +    heading_strs = "", *[f"Socket {s}" for s in sockets]
> +    sep_strs = tuple("-" * len(hstr) for hstr in heading_strs)
> +    rows: List[Tuple[str, ...]] = []
> +
> +    for c in cores:
> +        # Core,
> +        row: Tuple[str, ...] = (f"Core {c}",)
> +
> +        # [lcores, lcores, ...]
> +        for s in sockets:
> +            try:
> +                lcores = core_map[(s, c)]
> +                row += (f"{lcores}",)
> +            except KeyError:
> +                row += ("",)
> +        rows += [row]
> +
> +    # find max widths for each column, including header and rows
> +    max_widths = [
> +        max([len(tup[col_idx]) for tup in rows + [heading_strs]])
> +        for col_idx in range(len(heading_strs))
> +    ]
> +
> +    # print out table taking row widths into account
> +    _print_row(heading_strs, max_widths)
> +    _print_row(sep_strs, max_widths)
> +    for row in rows:
> +        _print_row(row, max_widths)
> +
> +
> +if __name__ == "__main__":
> +    _main()
> -- 
> 2.43.5
  
Burakov, Anatoly Aug. 19, 2024, 9:36 a.m. UTC | #2
On 8/19/2024 11:26 AM, Robin Jarry wrote:
> Anatoly Burakov, Aug 14, 2024 at 13:19:
>> Update coding style:
>>
>> - make it PEP-484 compliant
>> - address all flake8, mypy etc. warnings
>> - use f-strings in place of old-style string interpolation
>> - refactor printing to make the code more readable
>>
>> Signed-off-by: Anatoly Burakov <anatoly.burakov@intel.com>
>> ---
> 
> Hi Anatoly,
> 
> thanks for the patch. Did you format the code using black/ruff? I'd like 
> to start keeping python code formatting consistent across user tools.

I don't think I did any formatting with a specific tool. Rather, my IDE 
had flake8 set up to give me warnings if there are issues with 
formatting, and it also does formatting, so this would effectively be 
the same thing.

Judging by description of Ruff, it sounds like something I should try 
out, so thanks for the suggestion.

> 
>>  usertools/cpu_layout.py | 162 ++++++++++++++++++++++++++--------------
>>  1 file changed, 104 insertions(+), 58 deletions(-)
>>
>> diff --git a/usertools/cpu_layout.py b/usertools/cpu_layout.py
>> index 891b9238fa..843b29a134 100755
>> --- a/usertools/cpu_layout.py
>> +++ b/usertools/cpu_layout.py
>> @@ -3,62 +3,108 @@
>>  # Copyright(c) 2010-2014 Intel Corporation
>>  # Copyright(c) 2017 Cavium, Inc. All rights reserved.
>>
>> -output = " ".ljust(max_core_id_len + len('Core '))
>> -for s in sockets:
>> -    output += " --------".ljust(max_core_map_len)
>> -    output += " "
>> -print(output)
>> -
>> -for c in cores:
>> -    output = "Core %s" % str(c).ljust(max_core_id_len)
>> -    for s in sockets:
>> -        if (s, c) in core_map:
>> -            output += " " + str(core_map[(s, 
>> c)]).ljust(max_core_map_len)
>> +from typing import List, Set, Dict, Tuple
>> +
>> +
>> +def _range_expand(rstr: str) -> List[int]:
> 
> I don't see any reason to prefix the symbols with an underscore in this 
> script. It will not be used as an importable library.

In the general case I prefer not to make such assumptions, but in this 
particular case I think it's a safe assumption to make.
  
Stephen Hemminger Aug. 19, 2024, 3:13 p.m. UTC | #3
On Mon, 19 Aug 2024 11:36:44 +0200
"Burakov, Anatoly" <anatoly.burakov@intel.com> wrote:

> On 8/19/2024 11:26 AM, Robin Jarry wrote:
> > Anatoly Burakov, Aug 14, 2024 at 13:19:  
> >> Update coding style:
> >>
> >> - make it PEP-484 compliant
> >> - address all flake8, mypy etc. warnings
> >> - use f-strings in place of old-style string interpolation
> >> - refactor printing to make the code more readable
> >>
> >> Signed-off-by: Anatoly Burakov <anatoly.burakov@intel.com>
> >> ---  
> > 
> > Hi Anatoly,
> > 
> > thanks for the patch. Did you format the code using black/ruff? I'd like 
> > to start keeping python code formatting consistent across user tools.  
> 
> I don't think I did any formatting with a specific tool. Rather, my IDE 
> had flake8 set up to give me warnings if there are issues with 
> formatting, and it also does formatting, so this would effectively be 
> the same thing.


I tend to use yapf3
  

Patch

diff --git a/usertools/cpu_layout.py b/usertools/cpu_layout.py
index 891b9238fa..843b29a134 100755
--- a/usertools/cpu_layout.py
+++ b/usertools/cpu_layout.py
@@ -3,62 +3,108 @@ 
 # Copyright(c) 2010-2014 Intel Corporation
 # Copyright(c) 2017 Cavium, Inc. All rights reserved.
 
-sockets = []
-cores = []
-core_map = {}
-base_path = "/sys/devices/system/cpu"
-fd = open("{}/kernel_max".format(base_path))
-max_cpus = int(fd.read())
-fd.close()
-for cpu in range(max_cpus + 1):
-    try:
-        fd = open("{}/cpu{}/topology/core_id".format(base_path, cpu))
-    except IOError:
-        continue
-    core = int(fd.read())
-    fd.close()
-    fd = open("{}/cpu{}/topology/physical_package_id".format(base_path, cpu))
-    socket = int(fd.read())
-    fd.close()
-    if core not in cores:
-        cores.append(core)
-    if socket not in sockets:
-        sockets.append(socket)
-    key = (socket, core)
-    if key not in core_map:
-        core_map[key] = []
-    core_map[key].append(cpu)
-
-print(format("=" * (47 + len(base_path))))
-print("Core and Socket Information (as reported by '{}')".format(base_path))
-print("{}\n".format("=" * (47 + len(base_path))))
-print("cores = ", cores)
-print("sockets = ", sockets)
-print("")
-
-max_processor_len = len(str(len(cores) * len(sockets) * 2 - 1))
-max_thread_count = len(list(core_map.values())[0])
-max_core_map_len = (max_processor_len * max_thread_count)  \
-                      + len(", ") * (max_thread_count - 1) \
-                      + len('[]') + len('Socket ')
-max_core_id_len = len(str(max(cores)))
-
-output = " ".ljust(max_core_id_len + len('Core '))
-for s in sockets:
-    output += " Socket %s" % str(s).ljust(max_core_map_len - len('Socket '))
-print(output)
-
-output = " ".ljust(max_core_id_len + len('Core '))
-for s in sockets:
-    output += " --------".ljust(max_core_map_len)
-    output += " "
-print(output)
-
-for c in cores:
-    output = "Core %s" % str(c).ljust(max_core_id_len)
-    for s in sockets:
-        if (s, c) in core_map:
-            output += " " + str(core_map[(s, c)]).ljust(max_core_map_len)
+from typing import List, Set, Dict, Tuple
+
+
+def _range_expand(rstr: str) -> List[int]:
+    """Expand a range string into a list of integers."""
+    # 0,1-3 => [0, 1-3]
+    ranges = rstr.split(",")
+    valset: List[int] = []
+    for r in ranges:
+        # 1-3 => [1, 2, 3]
+        if "-" in r:
+            start, end = r.split("-")
+            valset.extend(range(int(start), int(end) + 1))
         else:
-            output += " " * (max_core_map_len + 1)
-    print(output)
+            valset.append(int(r))
+    return valset
+
+
+def _read_sysfs(path: str) -> str:
+    with open(path) as fd:
+        return fd.read().strip()
+
+
+def _print_row(row: Tuple[str, ...], col_widths: List[int]) -> None:
+    first, *rest = row
+    w_first, *w_rest = col_widths
+    first_end = " " * 4
+    rest_end = " " * 10
+
+    print(first.ljust(w_first), end=first_end)
+    for cell, width in zip(rest, w_rest):
+        print(cell.rjust(width), end=rest_end)
+    print()
+
+
+def _print_section(heading: str) -> None:
+    sep = "=" * len(heading)
+    print(sep)
+    print(heading)
+    print(sep)
+    print()
+
+
+def _main() -> None:
+    sockets_s: Set[int] = set()
+    cores_s: Set[int] = set()
+    core_map: Dict[Tuple[int, int], List[int]] = {}
+    base_path = "/sys/devices/system/cpu"
+
+    cpus = _range_expand(_read_sysfs(f"{base_path}/online"))
+
+    for cpu in cpus:
+        lcore_base = f"{base_path}/cpu{cpu}"
+        core = int(_read_sysfs(f"{lcore_base}/topology/core_id"))
+        socket = int(_read_sysfs(f"{lcore_base}/topology/physical_package_id"))
+
+        cores_s.add(core)
+        sockets_s.add(socket)
+        key = (socket, core)
+        core_map.setdefault(key, [])
+        core_map[key].append(cpu)
+
+    cores = sorted(cores_s)
+    sockets = sorted(sockets_s)
+
+    _print_section("Core and Socket Information "
+                   f"(as reported by '{base_path}')")
+
+    print("cores = ", cores)
+    print("sockets = ", sockets)
+    print()
+
+    # Core, [Socket, Socket, ...]
+    heading_strs = "", *[f"Socket {s}" for s in sockets]
+    sep_strs = tuple("-" * len(hstr) for hstr in heading_strs)
+    rows: List[Tuple[str, ...]] = []
+
+    for c in cores:
+        # Core,
+        row: Tuple[str, ...] = (f"Core {c}",)
+
+        # [lcores, lcores, ...]
+        for s in sockets:
+            try:
+                lcores = core_map[(s, c)]
+                row += (f"{lcores}",)
+            except KeyError:
+                row += ("",)
+        rows += [row]
+
+    # find max widths for each column, including header and rows
+    max_widths = [
+        max([len(tup[col_idx]) for tup in rows + [heading_strs]])
+        for col_idx in range(len(heading_strs))
+    ]
+
+    # print out table taking row widths into account
+    _print_row(heading_strs, max_widths)
+    _print_row(sep_strs, max_widths)
+    for row in rows:
+        _print_row(row, max_widths)
+
+
+if __name__ == "__main__":
+    _main()