Linux Kernel Selftest development
 help / color / mirror / Atom feed
* [PATCH net-next v3 0/4] selftests: openvswitch: pylint cleanup of ovs-dpctl.py
@ 2026-09-08 17:18 Minxi Hou
  2026-09-08 17:18 ` [PATCH net-next v3 1/4] selftests: openvswitch: convert %-formatting to f-strings Minxi Hou
                   ` (3 more replies)
  0 siblings, 4 replies; 8+ messages in thread
From: Minxi Hou @ 2026-09-08 17:18 UTC (permalink / raw)
  To: netdev
  Cc: aconole, echaudro, i.maximets, davem, edumazet, kuba, pabeni,
	horms, shuah, dev, linux-kselftest, Minxi Hou

Hi,

This is a respin of the ovs-dpctl.py pylint cleanup series. The series
raises the pylint score of ovs-dpctl.py from 7.69 to 10.00. Each patch
targets one warning class and makes no behavior change.

v2 -> v3:
- Rewrote patch 3/4 (docstrings). In v2 many of the added docstrings
  were shifted by one method and described the wrong operation (e.g.
  OvsDatapath.create carried "Destroy a datapath."). Every docstring
  is now derived from the method it documents.
- Dropped the ctact -> CtAct class rename and the parsed_len ->
  parsedLen variable rename that had leaked into patches 2/4 and 3/4.
  Class and variable names now stay as upstream has them; the
  corresponding invalid-name warnings are covered by the module-level
  disable in patch 4/4 instead.
- Moved three hunks to the patches they belong to: one f-string
  conversion went to patch 1/4, the bare-except fix and the two
  @staticmethod decorators went to patch 2/4 (the v2 patch 2/4
  changelog claimed the except fix but the hunk itself sat in 3/4).
- Rebased onto ab217fbb9b21 ("Merge branch 'net-sysfs-use-ops-lock-
  for-speed-and-duplex'") to resolve the pw-ci contest conflict.

v2: https://lore.kernel.org/netdev/20260905104026.3776396-1-houminxi@gmail.com/
v1: https://lore.kernel.org/netdev/20260513121240.2590767-1-houminxi@gmail.com/

Thanks,
Minxi

Signed-off-by: Minxi Hou <houminxi@gmail.com>

Minxi Hou (4):
  selftests: openvswitch: convert %-formatting to f-strings
  selftests: openvswitch: fix misc pylint warnings in ovs-dpctl.py
  selftests: openvswitch: add missing docstrings in ovs-dpctl.py
  selftests: openvswitch: suppress pylint complexity warnings

 .../selftests/net/openvswitch/ovs-dpctl.py    | 408 ++++++++++--------
 1 file changed, 236 insertions(+), 172 deletions(-)


base-commit: ab217fbb9b2169ce677b09a66558d5c3adcfbb76
-- 
2.55.0


^ permalink raw reply	[flat|nested] 8+ messages in thread

* [PATCH net-next v3 1/4] selftests: openvswitch: convert %-formatting to f-strings
  2026-09-08 17:18 [PATCH net-next v3 0/4] selftests: openvswitch: pylint cleanup of ovs-dpctl.py Minxi Hou
@ 2026-09-08 17:18 ` Minxi Hou
  2026-09-08 17:18 ` [PATCH net-next v3 2/4] selftests: openvswitch: fix misc pylint warnings in ovs-dpctl.py Minxi Hou
                   ` (2 subsequent siblings)
  3 siblings, 0 replies; 8+ messages in thread
From: Minxi Hou @ 2026-09-08 17:18 UTC (permalink / raw)
  To: netdev
  Cc: aconole, echaudro, i.maximets, davem, edumazet, kuba, pabeni,
	horms, shuah, dev, linux-kselftest, Minxi Hou

Convert all remaining instances of C0209 %-formatting to f-strings
in ovs-dpctl.py. No behavior change.

Signed-off-by: Minxi Hou <houminxi@gmail.com>
---
 .../selftests/net/openvswitch/ovs-dpctl.py    | 269 ++++++++----------
 1 file changed, 121 insertions(+), 148 deletions(-)

diff --git a/tools/testing/selftests/net/openvswitch/ovs-dpctl.py b/tools/testing/selftests/net/openvswitch/ovs-dpctl.py
index 1615843c225e4..cac858dae4ce9 100644
--- a/tools/testing/selftests/net/openvswitch/ovs-dpctl.py
+++ b/tools/testing/selftests/net/openvswitch/ovs-dpctl.py
@@ -65,7 +65,7 @@ OVS_FLOW_CMD_SET = 4
 UINT32_MAX = 0xFFFFFFFF
 
 def macstr(mac):
-    outstr = ":".join(["%02X" % i for i in mac])
+    outstr = ":".join([f"{i:02X}" for i in mac])
     return outstr
 
 
@@ -146,7 +146,7 @@ def parse_flags(flag_str, flag_vals):
         if flag in flag_vals:
             if maskResult & flag_vals[flag]:
                 raise KeyError(
-                    "Flag %s set once, cannot be set in multiples" % flag
+                    f"Flag {flag} set once, cannot be set in multiples"
                 )
 
             if setFlag:
@@ -154,7 +154,7 @@ def parse_flags(flag_str, flag_vals):
 
             maskResult |= flag_vals[flag]
         else:
-            raise KeyError("Missing flag value: %s" % flag)
+            raise KeyError(f"Missing flag value: {flag}")
 
         flag_str = flag_str[flag_len:]
 
@@ -211,7 +211,7 @@ def convert_ipv6(data):
     elif not mask:
         mask = 'ffff:ffff:ffff:ffff:ffff:ffff:ffff:ffff'
     elif mask.isdigit():
-        mask = ipaddress.IPv6Network("::/" + mask).hostmask
+        mask = ipaddress.IPv6Network(f"::/{mask}").hostmask
 
     return ipaddress.IPv6Address(ip).packed, ipaddress.IPv6Address(mask).packed
 
@@ -342,13 +342,13 @@ def parse_attrs(actstr, attr_desc):
                 del attr_desc[i]
 
         if not found:
-            raise ValueError("Unknown attribute: '%s'" % actstr)
+            raise ValueError(f"Unknown attribute: '{actstr}'")
 
         actstr = actstr[strspn(actstr, ", ") :]
 
     if actstr[0] != ")":
         raise ValueError("Action string contains extra garbage or has "
-                         "unbalanced parenthesis: '%s'" % actstr)
+                         f"unbalanced parenthesis: '{actstr}'")
 
     return attrs, actstr[1:]
 
@@ -413,14 +413,13 @@ class ovsactions(nla):
         )
 
         def dpstr(self, more=False):
-            args = "group=%d" % self.get_attr("OVS_PSAMPLE_ATTR_GROUP")
+            args = f"group={int(self.get_attr('OVS_PSAMPLE_ATTR_GROUP'))}"
 
             cookie = self.get_attr("OVS_PSAMPLE_ATTR_COOKIE")
             if cookie:
-                args += ",cookie(%s)" % \
-                        "".join(format(x, "02x") for x in cookie)
+                args += f",cookie({''.join((format(x, '02x') for x in cookie))})"
 
-            return "psample(%s)" % args
+            return f"psample({args})"
 
         def parse(self, actstr):
             desc = (
@@ -451,15 +450,15 @@ class ovsactions(nla):
         def dpstr(self, more=False):
             args = []
 
-            args.append("sample={:.2f}%".format(
-                100 * self.get_attr("OVS_SAMPLE_ATTR_PROBABILITY") /
-                UINT32_MAX))
+            prob = 100 * self.get_attr(
+                "OVS_SAMPLE_ATTR_PROBABILITY") / UINT32_MAX
+            args.append(f"sample={prob:.2f}%")
 
             actions = self.get_attr("OVS_SAMPLE_ATTR_ACTIONS")
             if actions:
-                args.append("actions(%s)" % actions.dpstr(more))
+                args.append(f"actions({actions.dpstr(more)})")
 
-            return "sample(%s)" % ",".join(args)
+            return f"sample({','.join(args)})"
 
         def parse(self, actstr):
             def parse_nested_actions(actstr):
@@ -528,26 +527,20 @@ class ovsactions(nla):
                     "OVS_NAT_ATTR_IP_MAX"
                 ):
                     if self.get_attr("OVS_NAT_ATTR_IP_MIN"):
-                        print_str += "=%s," % str(
-                            self.get_attr("OVS_NAT_ATTR_IP_MIN")
-                        )
+                        print_str += f"={self.get_attr('OVS_NAT_ATTR_IP_MIN')!s},"
 
                     if self.get_attr("OVS_NAT_ATTR_IP_MAX"):
-                        print_str += "-%s," % str(
-                            self.get_attr("OVS_NAT_ATTR_IP_MAX")
-                        )
+                        print_str += f"-{self.get_attr('OVS_NAT_ATTR_IP_MAX')!s},"
                 else:
                     print_str += ","
 
                 if self.get_attr("OVS_NAT_ATTR_PROTO_MIN"):
-                    print_str += "proto_min=%d," % self.get_attr(
-                        "OVS_NAT_ATTR_PROTO_MIN"
-                    )
+                    val = self.get_attr("OVS_NAT_ATTR_PROTO_MIN")
+                    print_str += f"proto_min={val},"
 
                 if self.get_attr("OVS_NAT_ATTR_PROTO_MAX"):
-                    print_str += "proto_max=%d," % self.get_attr(
-                        "OVS_NAT_ATTR_PROTO_MAX"
-                    )
+                    val = self.get_attr("OVS_NAT_ATTR_PROTO_MAX")
+                    print_str += f"proto_max={val},"
 
                 if self.get_attr("OVS_NAT_ATTR_PERSISTENT"):
                     print_str += "persistent,"
@@ -564,22 +557,18 @@ class ovsactions(nla):
             if self.get_attr("OVS_CT_ATTR_COMMIT") is not None:
                 print_str += "commit,"
             if self.get_attr("OVS_CT_ATTR_ZONE") is not None:
-                print_str += "zone=%d," % self.get_attr("OVS_CT_ATTR_ZONE")
+                print_str += f"zone={int(self.get_attr('OVS_CT_ATTR_ZONE'))},"
             if self.get_attr("OVS_CT_ATTR_HELPER") is not None:
-                print_str += "helper=%s," % self.get_attr("OVS_CT_ATTR_HELPER")
+                print_str += f"helper={self.get_attr('OVS_CT_ATTR_HELPER')},"
             if self.get_attr("OVS_CT_ATTR_NAT") is not None:
                 print_str += self.get_attr("OVS_CT_ATTR_NAT").dpstr(more)
                 print_str += ","
             if self.get_attr("OVS_CT_ATTR_FORCE_COMMIT") is not None:
                 print_str += "force,"
             if self.get_attr("OVS_CT_ATTR_EVENTMASK") is not None:
-                print_str += "emask=0x%X," % self.get_attr(
-                    "OVS_CT_ATTR_EVENTMASK"
-                )
+                print_str += f"emask=0x{self.get_attr('OVS_CT_ATTR_EVENTMASK'):X},"
             if self.get_attr("OVS_CT_ATTR_TIMEOUT") is not None:
-                print_str += "timeout=%s" % self.get_attr(
-                    "OVS_CT_ATTR_TIMEOUT"
-                )
+                print_str += f"timeout={self.get_attr('OVS_CT_ATTR_TIMEOUT')}"
             print_str += ")"
             return print_str
 
@@ -596,17 +585,15 @@ class ovsactions(nla):
         def dpstr(self, more=False):
             print_str = "userspace("
             if self.get_attr("OVS_USERSPACE_ATTR_PID") is not None:
-                print_str += "pid=%d," % self.get_attr(
-                    "OVS_USERSPACE_ATTR_PID"
-                )
+                print_str += f"pid={int(self.get_attr('OVS_USERSPACE_ATTR_PID'))},"
             if self.get_attr("OVS_USERSPACE_ATTR_USERDATA") is not None:
                 print_str += "userdata="
                 for f in self.get_attr("OVS_USERSPACE_ATTR_USERDATA"):
-                    print_str += "%x." % f
+                    print_str += f"{f:x}."
             if self.get_attr("OVS_USERSPACE_ATTR_EGRESS_TUN_PORT") is not None:
-                print_str += "egress_tun_port=%d" % self.get_attr(
-                    "OVS_USERSPACE_ATTR_EGRESS_TUN_PORT"
-                )
+                val = self.get_attr(
+                    "OVS_USERSPACE_ATTR_EGRESS_TUN_PORT")
+                print_str += f"egress_tun_port={val}"
             print_str += ")"
             return print_str
 
@@ -634,13 +621,13 @@ class ovsactions(nla):
                 print_str += ","
 
             if field[0] == "OVS_ACTION_ATTR_OUTPUT":
-                print_str += "%d" % int(self.get_attr(field[0]))
+                print_str += f"{int(self.get_attr(field[0]))}"
             elif field[0] == "OVS_ACTION_ATTR_RECIRC":
-                print_str += "recirc(0x%x)" % int(self.get_attr(field[0]))
+                print_str += f"recirc(0x{int(self.get_attr(field[0])):x})"
             elif field[0] == "OVS_ACTION_ATTR_TRUNC":
-                print_str += "trunc(%d)" % int(self.get_attr(field[0]))
+                print_str += f"trunc({int(self.get_attr(field[0]))})"
             elif field[0] == "OVS_ACTION_ATTR_DROP":
-                print_str += "drop(%d)" % int(self.get_attr(field[0]))
+                print_str += f"drop({int(self.get_attr(field[0]))})"
             elif field[0] == "OVS_ACTION_ATTR_CT_CLEAR":
                 print_str += "ct_clear"
             elif field[0] == "OVS_ACTION_ATTR_POP_VLAN":
@@ -658,8 +645,8 @@ class ovsactions(nla):
                 tci = datum["vlan_tci"]
                 vid = tci & 0x0FFF
                 pcp = (tci >> 13) & 0x7
-                print_str += "push_vlan(vid=%d,pcp=%d" \
-                    ",tpid=0x%04x)" % (vid, pcp, tpid)
+                print_str += (f"push_vlan(vid={vid},pcp={pcp}"
+                              f",tpid=0x{tpid:04x})")
             elif field[0] == "OVS_ACTION_ATTR_POP_ETH":
                 print_str += "pop_eth"
             elif field[0] == "OVS_ACTION_ATTR_POP_NSH":
@@ -688,7 +675,7 @@ class ovsactions(nla):
                     try:
                         print_str += datum.dpstr(more)
                     except:
-                        print_str += "{ATTR: %s not decoded}" % field[0]
+                        print_str += f"{{ATTR: {field[0]} not decoded}}"
 
         return print_str
 
@@ -767,32 +754,27 @@ class ovsactions(nla):
                 for kv in actstr[:paren].split(","):
                     if "=" not in kv:
                         raise ValueError(
-                            "push_vlan(): bad field '%s'"
-                            % kv.strip())
+                            f"push_vlan(): bad field '{kv.strip()}'")
                     k = kv[:kv.index("=")].strip()
                     v = kv[kv.index("=") + 1:].strip()
                     if k == "vid":
                         vid = int(v, 0)
                         if vid < 0 or vid > 0xFFF:
                             raise ValueError(
-                                "push_vlan(): vid=%d out of "
-                                "range (0-4095)" % vid)
+                                f"push_vlan(): vid={int(vid)} out of range (0-4095)")
                     elif k == "pcp":
                         pcp = int(v, 0)
                         if pcp < 0 or pcp > 7:
                             raise ValueError(
-                                "push_vlan(): pcp=%d out of "
-                                "range (0-7)" % pcp)
+                                f"push_vlan(): pcp={int(pcp)} out of range (0-7)")
                     elif k == "tpid":
                         tpid = int(v, 0)
                         if tpid < 0 or tpid > 0xFFFF:
                             raise ValueError(
-                                "push_vlan(): tpid=0x%x out "
-                                "of range (0-0xffff)" % tpid)
+                                f"push_vlan(): tpid=0x{tpid:x} out of range (0-0xffff)")
                     else:
                         raise ValueError(
-                            "push_vlan(): unknown key '%s'"
-                            % k)
+                            f"push_vlan(): unknown key '{k}'")
                 tci = (vid & 0x0FFF) | ((pcp & 0x7) << 13) \
                     | 0x1000
                 pvact = self.push_vlan()
@@ -833,7 +815,7 @@ class ovsactions(nla):
                 actstr = k.parse(actstr, None)
                 self["attrs"].append(("OVS_ACTION_ATTR_SET", k))
                 if not actstr.startswith(")"):
-                    actstr = ")" + actstr
+                    actstr = f"){actstr}"
                 parsed = True
             elif parse_starts_block(actstr, "set_masked(", False):
                 parencount += 1
@@ -843,7 +825,7 @@ class ovsactions(nla):
                 actstr = k.parse(actstr, m)
                 self["attrs"].append(("OVS_ACTION_ATTR_SET_MASKED", [k, m]))
                 if not actstr.startswith(")"):
-                    actstr = ")" + actstr
+                    actstr = f"){actstr}"
                 parsed = True
             elif parse_starts_block(actstr, "ct(", False):
                 parencount += 1
@@ -974,7 +956,7 @@ class ovsactions(nla):
                 parencount -= 1
                 actstr = actstr[strspn(actstr, " "):]
                 if len(actstr) and actstr[0] != ")":
-                    raise ValueError("Action str: '%s' unbalanced" % actstr)
+                    raise ValueError(f"Action str: '{actstr}' unbalanced")
                 actstr = actstr[1:]
 
             if len(actstr) and actstr[0] == ")":
@@ -983,7 +965,7 @@ class ovsactions(nla):
             actstr = actstr[strspn(actstr, ", ") :]
 
             if not parsed:
-                raise ValueError("Action str: '%s' not supported" % actstr)
+                raise ValueError(f"Action str: '{actstr}' not supported")
 
         return (totallen - len(actstr))
 
@@ -1108,20 +1090,20 @@ class ovskey(nla):
             return flowstr, k, m
 
         def dpstr(self, masked=None, more=False):
-            outstr = self.proto_str + "("
+            outstr = f"{self.proto_str}("
             first = False
             for f in self.fields_map:
                 if first:
                     outstr += ","
                 if masked is None:
-                    outstr += "%s=" % f[0]
+                    outstr += f"{f[0]}="
                     if isinstance(f[2], str):
                         outstr += f[2] % self[f[1]]
                     else:
                         outstr += f[2](self[f[1]])
                     first = True
                 elif more or f[3](masked[f[1]]) != 0:
-                    outstr += "%s=" % f[0]
+                    outstr += f"{f[0]}="
                     if isinstance(f[2], str):
                         outstr += f[2] % self[f[1]]
                     else:
@@ -1702,23 +1684,23 @@ class ovskey(nla):
             for k in self["attrs"]:
                 noprint = False
                 if k[0] == "OVS_TUNNEL_KEY_ATTR_ID":
-                    print_str += "tun_id=%d" % k[1]
+                    print_str += f"tun_id={int(k[1])}"
                 elif k[0] == "OVS_TUNNEL_KEY_ATTR_IPV4_SRC":
-                    print_str += "src=%s" % k[1]
+                    print_str += f"src={k[1]}"
                 elif k[0] == "OVS_TUNNEL_KEY_ATTR_IPV4_DST":
-                    print_str += "dst=%s" % k[1]
+                    print_str += f"dst={k[1]}"
                 elif k[0] == "OVS_TUNNEL_KEY_ATTR_IPV6_SRC":
-                    print_str += "ipv6_src=%s" % k[1]
+                    print_str += f"ipv6_src={k[1]}"
                 elif k[0] == "OVS_TUNNEL_KEY_ATTR_IPV6_DST":
-                    print_str += "ipv6_dst=%s" % k[1]
+                    print_str += f"ipv6_dst={k[1]}"
                 elif k[0] == "OVS_TUNNEL_KEY_ATTR_TOS":
-                    print_str += "tos=%d" % k[1]
+                    print_str += f"tos={int(k[1])}"
                 elif k[0] == "OVS_TUNNEL_KEY_ATTR_TTL":
-                    print_str += "ttl=%d" % k[1]
+                    print_str += f"ttl={int(k[1])}"
                 elif k[0] == "OVS_TUNNEL_KEY_ATTR_TP_SRC":
-                    print_str += "tp_src=%d" % k[1]
+                    print_str += f"tp_src={int(k[1])}"
                 elif k[0] == "OVS_TUNNEL_KEY_ATTR_TP_DST":
-                    print_str += "tp_dst=%d" % k[1]
+                    print_str += f"tp_dst={int(k[1])}"
                 elif k[0] == "OVS_TUNNEL_KEY_ATTR_DONT_FRAGMENT":
                     noprint = True
                     flagsattrs.append("df")
@@ -1733,7 +1715,7 @@ class ovskey(nla):
                     print_str += ","
 
             if len(flagsattrs):
-                print_str += "flags(" + "|".join(flagsattrs) + ")"
+                print_str += f"flags({'|'.join(flagsattrs)})"
             print_str += ")"
             return print_str
 
@@ -1756,8 +1738,8 @@ class ovskey(nla):
         pcp = (tci >> 13) & 0x7
         cfi = (tci >> 12) & 0x1
         if cfi:
-            return "vid=%d,pcp=%d,cfi=%d" % (vid, pcp, cfi)
-        return "tci=0x%04x" % tci
+            return f"vid={int(vid)},pcp={int(pcp)},cfi={int(cfi)}"
+        return f"tci=0x{tci:04x}"
 
     @staticmethod
     def _parse_vlan_from_flowstr(flowstr):
@@ -1801,7 +1783,7 @@ class ovskey(nla):
             eq = flowstr.find('=')
             if eq == -1:
                 raise ValueError(
-                    "vlan(): expected key=value, got '%s'" % flowstr)
+                    f"vlan(): expected key=value, got '{flowstr}'")
             key = flowstr[:eq].strip()
             flowstr = flowstr[eq + 1:]
 
@@ -1815,13 +1797,12 @@ class ovskey(nla):
             flowstr = flowstr[end:]
 
             if not val:
-                raise ValueError("vlan(): empty value for key '%s'" % key)
+                raise ValueError(f"vlan(): empty value for key '{key}'")
             try:
                 v = int(val, 0)
             except ValueError as exc:
                 raise ValueError(
-                    "vlan(): invalid value '%s' for key '%s'"
-                    % (val, key)) from exc
+                    f"vlan(): invalid value '{val}' for key '{key}'") from exc
 
             if key == 'tci':
                 if has_tci:
@@ -1829,7 +1810,7 @@ class ovskey(nla):
                 if has_vid or has_pcp or has_cfi:
                     raise ValueError(_tci_mix_err)
                 if v > 0xFFFF or v < 0:
-                    raise ValueError("vlan(): tci=0x%x out of range" % v)
+                    raise ValueError(f"vlan(): tci=0x{v:x} out of range")
                 tci = v
                 mask = 0xFFFF
                 has_tci = True
@@ -1839,7 +1820,7 @@ class ovskey(nla):
                 if has_vid:
                     raise ValueError("vlan(): duplicate 'vid'")
                 if v < 0 or v > 0xFFF:
-                    raise ValueError("vlan(): vid=%d out of range (0-4095)" % v)
+                    raise ValueError(f"vlan(): vid={int(v)} out of range (0-4095)")
                 tci |= v
                 mask |= 0x0FFF
                 has_vid = True
@@ -1849,7 +1830,7 @@ class ovskey(nla):
                 if has_pcp:
                     raise ValueError("vlan(): duplicate 'pcp'")
                 if v < 0 or v > 7:
-                    raise ValueError("vlan(): pcp=%d out of range (0-7)" % v)
+                    raise ValueError(f"vlan(): pcp={int(v)} out of range (0-7)")
                 tci |= (v & 0x7) << 13
                 mask |= 0xE000
                 has_pcp = True
@@ -1864,7 +1845,7 @@ class ovskey(nla):
                 mask |= ovskey._VLAN_CFI_MASK
                 has_cfi = True
             else:
-                raise ValueError("vlan(): unknown key '%s'" % key)
+                raise ValueError(f"vlan(): unknown key '{key}'")
 
         flowstr = flowstr[1:]  # skip ')'
         # Catch immediate '))' (user error).  A ')' after ',' is consumed
@@ -1900,7 +1881,7 @@ class ovskey(nla):
                 depth -= 1
                 if depth < 0:
                     raise ValueError(
-                        "encap(): unmatched ')' at position %d" % i)
+                        f"encap(): unmatched ')' at position {int(i)}")
                 if depth == 0:
                     end = i
                     break
@@ -1923,8 +1904,7 @@ class ovskey(nla):
         remaining = inner_key.parse(inner_str, inner_mask)
         if remaining and re.search(r'[^\s,)]', remaining):
             raise ValueError(
-                "encap(): unrecognized trailing "
-                "content '%s'" % remaining.strip())
+                f"encap(): unrecognized trailing content '{remaining.strip()}'")
 
         return flowstr, inner_key, inner_mask
 
@@ -2005,7 +1985,7 @@ class ovskey(nla):
                 lambda x: parse_flags(x, None),
             ),
         ):
-            fld = field[1] + "("
+            fld = f"{field[1]}("
             if not flowstr.startswith(fld):
                 continue
 
@@ -2143,15 +2123,15 @@ class ovskey(nla):
                 else:
                     if m is None or field[3](m):
                         val = fmt(v) if callable(fmt) else fmt % v
-                        print_str += field[1] + "(" + val + "),"
+                        print_str += f"{field[1]}({val}),"
                     elif more or m != 0:
                         if field[0] == "OVS_KEY_ATTR_VLAN":
-                            val = "tci=0x%04x/0x%04x" % (v, m)
+                            val = f"tci=0x{v:04x}/0x{m:04x}"
                         elif callable(fmt):
-                            val = fmt(v) + "/" + fmt(m)
+                            val = f"{fmt(v)}/{fmt(m)}"
                         else:
-                            val = (fmt % v) + "/" + (fmt % m)
-                        print_str += field[1] + "(" + val + "),"
+                            val = f"{fmt % v}/{fmt % m}"
+                        print_str += f"{field[1]}({val}),"
 
         return print_str
 
@@ -2233,7 +2213,7 @@ class OvsPacket(GenericNetlinkSocket):
                     elif msg["cmd"] == OvsPacket.OVS_PACKET_CMD_EXECUTE:
                         up.execute(msg)
                     else:
-                        print("Unknown cmd: %d" % msg["cmd"])
+                        print(f"Unknown cmd: {int(msg['cmd'])}")
             except NetlinkError as ne:
                 raise ne
 
@@ -2401,14 +2381,14 @@ class OvsVport(GenericNetlinkSocket):
             return "netdev"
         elif vport_type == OvsVport.OVS_VPORT_TYPE_INTERNAL:
             return "internal"
-        raise ValueError("Unknown vport type:%d" % vport_type)
+        raise ValueError(f"Unknown vport type:{int(vport_type)}")
 
     def str_to_type(vport_type):
         if vport_type in ["netdev", "gre", "vxlan", "geneve"]:
             return OvsVport.OVS_VPORT_TYPE_NETDEV
         elif vport_type == "internal":
             return OvsVport.OVS_VPORT_TYPE_INTERNAL
-        raise ValueError("Unknown vport type: '%s'" % vport_type)
+        raise ValueError(f"Unknown vport type: '{vport_type}'")
 
     def __init__(self, packet=OvsPacket()):
         GenericNetlinkSocket.__init__(self)
@@ -2569,16 +2549,14 @@ class OvsFlow(GenericNetlinkSocket):
             ufid = self.get_attr("OVS_FLOW_ATTR_UFID")
             ufid_str = ""
             if ufid is not None:
-                ufid_str = (
-                    "ufid:{:08x}-{:04x}-{:04x}-{:04x}-{:04x}{:08x}".format(
-                        ufid[0],
-                        ufid[1] >> 16,
-                        ufid[1] & 0xFFFF,
-                        ufid[2] >> 16,
-                        ufid[2] & 0,
-                        ufid[3],
-                    )
-                )
+                u0 = ufid[0]
+                u1h = ufid[1] >> 16
+                u1l = ufid[1] & 0xFFFF
+                u2h = ufid[2] >> 16
+                u2l = ufid[2] & 0
+                u3 = ufid[3]
+                ufid_str = (f"ufid:{u0:08x}-{u1h:04x}-{u1l:04x}"
+                            f"-{u2h:04x}-{u2l:04x}{u3:08x}")
 
             key_field = self.get_attr("OVS_FLOW_ATTR_KEY")
             keymsg = None
@@ -2598,7 +2576,7 @@ class OvsFlow(GenericNetlinkSocket):
             print_str = ""
 
             if more:
-                print_str += ufid_str + ","
+                print_str += f"{ufid_str},"
 
             if keymsg is not None:
                 print_str += keymsg.dpstr(maskmsg, more)
@@ -2607,10 +2585,9 @@ class OvsFlow(GenericNetlinkSocket):
             if stats is None:
                 print_str += " packets:0, bytes:0,"
             else:
-                print_str += " packets:%d, bytes:%d," % (
-                    stats["packets"],
-                    stats["bytes"],
-                )
+                pkts = stats["packets"]
+                nbytes = stats["bytes"]
+                print_str += f" packets:{pkts}, bytes:{nbytes},"
 
             used = self.get_attr("OVS_FLOW_ATTR_USED")
             print_str += " used:"
@@ -2620,7 +2597,7 @@ class OvsFlow(GenericNetlinkSocket):
                 used_time = int(used)
                 cur_time_sec = time.clock_gettime(time.CLOCK_MONOTONIC)
                 used_time = (cur_time_sec * 1000) - used_time
-                print_str += "{}s,".format(used_time / 1000)
+                print_str += f"{used_time / 1000}s,"
 
             print_str += " actions:"
             if (
@@ -2808,7 +2785,7 @@ class OvsFlow(GenericNetlinkSocket):
         pktdata = packetmsg.get_attr("OVS_PACKET_ATTR_PACKET")
         pktpres = "yes" if pktdata is not None else "no"
 
-        print("MISS upcall[%d/%s]: %s" % (seq, pktpres, keystr), flush=True)
+        print(f"MISS upcall[{int(seq)}/{pktpres}]: {keystr}", flush=True)
 
     def execute(self, packetmsg):
         print("userspace execute command", flush=True)
@@ -2842,16 +2819,16 @@ class psample_sample(genlmsg):
         data = ""
         for (attr, value) in self["attrs"]:
             if attr == "PSAMPLE_ATTR_SAMPLE_GROUP":
-                fields.append("group:%d" % value)
+                fields.append(f"group:{int(value)}")
             if attr == "PSAMPLE_ATTR_SAMPLE_RATE":
-                fields.append("rate:%d" % value)
+                fields.append(f"rate:{int(value)}")
             if attr == "PSAMPLE_ATTR_USER_COOKIE":
                 value = "".join(format(x, "02x") for x in value)
-                fields.append("cookie:%s" % value)
+                fields.append(f"cookie:{value}")
             if attr == "PSAMPLE_ATTR_DATA" and len(value) > 0:
-                data = "data:%s" % "".join(format(x, "02x") for x in value)
+                data = f"data:{''.join((format(x, '02x') for x in value))}"
 
-        return ("%s %s" % (",".join(fields), data)).strip()
+        return (f"{','.join(fields)} {data}").strip()
 
 
 class psample_msg(Marshal):
@@ -2885,35 +2862,31 @@ def print_ovsdp_full(dp_lookup_rep, ifindex, ndb=NDB(), vpl=OvsVport()):
     user_features = dp_lookup_rep.get_attr("OVS_DP_ATTR_USER_FEATURES")
     masks_cache_size = dp_lookup_rep.get_attr("OVS_DP_ATTR_MASKS_CACHE_SIZE")
 
-    print("%s:" % dp_name)
-    print(
-        "  lookups: hit:%d missed:%d lost:%d"
-        % (base_stats["hit"], base_stats["missed"], base_stats["lost"])
-    )
-    print("  flows:%d" % base_stats["flows"])
+    print(f"{dp_name}:")
+    hit = base_stats["hit"]
+    missed = base_stats["missed"]
+    lost = base_stats["lost"]
+    print(f"  lookups: hit:{hit} missed:{missed} lost:{lost}")
+    print(f"  flows:{int(base_stats['flows'])}")
     pkts = base_stats["hit"] + base_stats["missed"]
     avg = (megaflow_stats["mask_hit"] / pkts) if pkts != 0 else 0.0
-    print(
-        "  masks: hit:%d total:%d hit/pkt:%f"
-        % (megaflow_stats["mask_hit"], megaflow_stats["masks"], avg)
-    )
+    mhit = megaflow_stats["mask_hit"]
+    mtotal = megaflow_stats["masks"]
+    print(f"  masks: hit:{mhit} total:{mtotal} hit/pkt:{avg:f}")
     print("  caches:")
-    print("    masks-cache: size:%d" % masks_cache_size)
+    print(f"    masks-cache: size:{int(masks_cache_size)}")
 
     if user_features is not None:
-        print("  features: 0x%X" % user_features)
+        print(f"  features: 0x{user_features:X}")
 
     # port print out
     for iface in ndb.interfaces:
         rep = vpl.info(iface.ifname, ifindex)
         if rep is not None:
             print(
-                "  port %d: %s (%s)"
-                % (
-                    rep.get_attr("OVS_VPORT_ATTR_PORT_NO"),
-                    rep.get_attr("OVS_VPORT_ATTR_NAME"),
-                    OvsVport.type_to_str(rep.get_attr("OVS_VPORT_ATTR_TYPE")),
-                )
+                f"  port {int(rep.get_attr('OVS_VPORT_ATTR_PORT_NO'))}: "
+                f"{rep.get_attr('OVS_VPORT_ATTR_NAME')} "
+                f"({OvsVport.type_to_str(rep.get_attr('OVS_VPORT_ATTR_TYPE'))})"
             )
 
 
@@ -3045,14 +3018,14 @@ def main(argv):
         if not found:
             msg = "No DP found"
             if args.showdp is not None:
-                msg += ":'%s'" % args.showdp
+                msg += f":'{args.showdp}'"
             print(msg)
     elif hasattr(args, "adddp"):
         rep = ovsdp.create(args.adddp, args.upcall, args.versioning, ovspk)
         if rep is None:
-            print("DP '%s' already exists" % args.adddp)
+            print(f"DP '{args.adddp}' already exists")
         else:
-            print("DP '%s' added" % args.adddp)
+            print(f"DP '{args.adddp}' added")
         if args.upcall:
             ovspk.upcall_handler(ovsflow)
     elif hasattr(args, "deldp"):
@@ -3060,12 +3033,12 @@ def main(argv):
     elif hasattr(args, "addif"):
         rep = ovsdp.info(args.dpname, 0)
         if rep is None:
-            print("DP '%s' not found." % args.dpname)
+            print(f"DP '{args.dpname}' not found.")
             return 1
         dpindex = rep["dpifindex"]
         rep = ovsvp.attach(rep["dpifindex"], args.addif, args.ptype,
                            args.dport)
-        msg = "vport '%s'" % args.addif
+        msg = f"vport '{args.addif}'"
         if rep and rep["header"]["error"] is None:
             msg += " added."
         else:
@@ -3077,10 +3050,10 @@ def main(argv):
     elif hasattr(args, "delif"):
         rep = ovsdp.info(args.dpname, 0)
         if rep is None:
-            print("DP '%s' not found." % args.dpname)
+            print(f"DP '{args.dpname}' not found.")
             return 1
         rep = ovsvp.detach(rep["dpifindex"], args.delif)
-        msg = "vport '%s'" % args.delif
+        msg = f"vport '{args.delif}'"
         if rep and rep["header"]["error"] is None:
             msg += " removed."
         else:
@@ -3091,7 +3064,7 @@ def main(argv):
     elif hasattr(args, "dumpdp"):
         rep = ovsdp.info(args.dumpdp, 0)
         if rep is None:
-            print("DP '%s' not found." % args.dumpdp)
+            print(f"DP '{args.dumpdp}' not found.")
             return 1
         rep = ovsflow.dump(rep["dpifindex"])
         for flow in rep:
@@ -3099,7 +3072,7 @@ def main(argv):
     elif hasattr(args, "flbr"):
         rep = ovsdp.info(args.flbr, 0)
         if rep is None:
-            print("DP '%s' not found." % args.flbr)
+            print(f"DP '{args.flbr}' not found.")
             return 1
         flow = OvsFlow.ovs_flow_msg()
         flow.parse(args.flow, args.acts, rep["dpifindex"])
@@ -3115,7 +3088,7 @@ def main(argv):
     elif hasattr(args, "flsbr"):
         rep = ovsdp.info(args.flsbr, 0)
         if rep is None:
-            print("DP '%s' not found." % args.flsbr)
+            print(f"DP '{args.flsbr}' not found.")
         ovsflow.del_flows(rep["dpifindex"])
 
     return 0
-- 
2.55.0


^ permalink raw reply related	[flat|nested] 8+ messages in thread

* [PATCH net-next v3 2/4] selftests: openvswitch: fix misc pylint warnings in ovs-dpctl.py
  2026-09-08 17:18 [PATCH net-next v3 0/4] selftests: openvswitch: pylint cleanup of ovs-dpctl.py Minxi Hou
  2026-09-08 17:18 ` [PATCH net-next v3 1/4] selftests: openvswitch: convert %-formatting to f-strings Minxi Hou
@ 2026-09-08 17:18 ` Minxi Hou
  2026-09-08 17:18 ` [PATCH net-next v3 3/4] selftests: openvswitch: add missing docstrings " Minxi Hou
  2026-09-08 17:18 ` [PATCH net-next v3 4/4] selftests: openvswitch: suppress pylint complexity warnings Minxi Hou
  3 siblings, 0 replies; 8+ messages in thread
From: Minxi Hou @ 2026-09-08 17:18 UTC (permalink / raw)
  To: netdev
  Cc: aconole, echaudro, i.maximets, davem, edumazet, kuba, pabeni,
	horms, shuah, dev, linux-kselftest, Minxi Hou

Fix miscellaneous pylint warnings with no behavior change:
  - W0611: remove unused import struct
  - W0702: replace bare except with except Exception
  - C0325: remove superfluous parentheses after return (3)
  - R1705: remove unnecessary elif after return (3)
  - W0108: replace unnecessary lambda with int
  - R1714: merge comparisons with in operator
  - W0719: replace raise Exception with raise ValueError
  - C1802: use implicit boolean test instead of len()
  - C0121: use is None instead of == None
  - R1719: simplify if-expression to bool test
  - R1703: simplify if/else to assignment expression
  - W0612: remove unused variables (keybits, maskbits, lst)
  - replace unused loop variable with underscore
  - E0213: add @staticmethod to type_to_str/str_to_type, both are
    only invoked through the class

Signed-off-by: Minxi Hou <houminxi@gmail.com>
---
 .../selftests/net/openvswitch/ovs-dpctl.py    | 48 +++++++++----------
 1 file changed, 22 insertions(+), 26 deletions(-)

diff --git a/tools/testing/selftests/net/openvswitch/ovs-dpctl.py b/tools/testing/selftests/net/openvswitch/ovs-dpctl.py
index cac858dae4ce9..502e6eb4c4b4a 100644
--- a/tools/testing/selftests/net/openvswitch/ovs-dpctl.py
+++ b/tools/testing/selftests/net/openvswitch/ovs-dpctl.py
@@ -11,7 +11,6 @@ import logging
 import math
 import multiprocessing
 import re
-import struct
 import sys
 import time
 import types
@@ -125,10 +124,7 @@ def parse_flags(flag_str, flag_vals):
         maskResult = int(digits, 0)
 
     while len(flag_str) > 0 and (flag_str[0] == "+" or flag_str[0] == "-"):
-        if flag_str[0] == "+":
-            setFlag = True
-        elif flag_str[0] == "-":
-            setFlag = False
+        setFlag = flag_str[0] == "+"
 
         flag_str = flag_str[1:]
 
@@ -221,10 +217,9 @@ def convert_int(size):
 
         if not value:
             return 0, 0
-        elif not mask:
+        if not mask:
             return int(value, 0), pow(2, size) - 1
-        else:
-            return int(value, 0), int(mask, 0)
+        return int(value, 0), int(mask, 0)
 
     return convert_int_sized
 
@@ -674,7 +669,7 @@ class ovsactions(nla):
                 else:
                     try:
                         print_str += datum.dpstr(more)
-                    except:
+                    except Exception:
                         print_str += f"{{ATTR: {field[0]} not decoded}}"
 
         return print_str
@@ -705,11 +700,11 @@ class ovsactions(nla):
                     parsed = True
                 else:
                     actstr = actstr[len("drop"): ]
-                    return (totallen - len(actstr))
+                    return totallen - len(actstr)
 
             elif parse_starts_block(actstr, r"^(\d+)", False, True):
                 actstr, output = parse_extract_field(
-                    actstr, None, r"(\d+)", lambda x: int(x), False, "0"
+                    actstr, None, r"(\d+)", int, False, "0"
                 )
                 self["attrs"].append(["OVS_ACTION_ATTR_OUTPUT", output])
                 parsed = True
@@ -761,12 +756,12 @@ class ovsactions(nla):
                         vid = int(v, 0)
                         if vid < 0 or vid > 0xFFF:
                             raise ValueError(
-                                f"push_vlan(): vid={int(vid)} out of range (0-4095)")
+                                f"push_vlan(): vid={vid} out of range (0-4095)")
                     elif k == "pcp":
                         pcp = int(v, 0)
                         if pcp < 0 or pcp > 7:
                             raise ValueError(
-                                f"push_vlan(): pcp={int(pcp)} out of range (0-7)")
+                                f"push_vlan(): pcp={pcp} out of range (0-7)")
                     elif k == "tpid":
                         tpid = int(v, 0)
                         if tpid < 0 or tpid > 0xFFFF:
@@ -804,7 +799,6 @@ class ovsactions(nla):
                 subacts = ovsactions()
                 actstr = actstr[len("clone("):]
                 parsedLen = subacts.parse(actstr)
-                lst = []
                 self["attrs"].append(("OVS_ACTION_ATTR_CLONE", subacts))
                 actstr = actstr[parsedLen:]
                 parsed = True
@@ -960,14 +954,14 @@ class ovsactions(nla):
                 actstr = actstr[1:]
 
             if len(actstr) and actstr[0] == ")":
-                return (totallen - len(actstr))
+                return totallen - len(actstr)
 
             actstr = actstr[strspn(actstr, ", ") :]
 
             if not parsed:
                 raise ValueError(f"Action str: '{actstr}' not supported")
 
-        return (totallen - len(actstr))
+        return totallen - len(actstr)
 
 
 # pyroute2 resolves nla_map types via getattr(self, name).
@@ -1057,8 +1051,6 @@ class ovskey(nla):
             if flowstr.startswith("("):
                 flowstr = flowstr[1:]
 
-            keybits = b""
-            maskbits = b""
             for f in self.fields_map:
                 if flowstr.startswith(f[1]):
                     # the following assumes that the field looks
@@ -1067,7 +1059,7 @@ class ovskey(nla):
                     flowstr = flowstr[len(f[1]) + 1 :]
                     splitchar = 0
                     for c in flowstr:
-                        if c == "," or c == ")":
+                        if c in (",", ")"):
                             break
                         splitchar += 1
                     data = flowstr[:splitchar]
@@ -1631,7 +1623,7 @@ class ovskey(nla):
             for prefix, regex, typ, attr_name, mask_val, default_val, v46_flag in fields:
                 flowstr, value = parse_extract_field(flowstr, prefix, regex, typ, False)
                 if not attr_name:
-                    raise Exception("Bad list value in tunnel fields")
+                    raise ValueError("Bad list value in tunnel fields")
 
                 if value is None and attr_name in forced_include:
                     value = default_val
@@ -1714,7 +1706,7 @@ class ovskey(nla):
                 if not noprint:
                     print_str += ","
 
-            if len(flagsattrs):
+            if flagsattrs:
                 print_str += f"flags({'|'.join(flagsattrs)})"
             print_str += ")"
             return print_str
@@ -2304,7 +2296,7 @@ class OvsDatapath(GenericNetlinkSocket):
 
             nproc = multiprocessing.cpu_count()
             procarray = []
-            for i in range(1, nproc):
+            for _ in range(1, nproc):
                 procarray += [int(p.epid)]
             msg["attrs"].append(["OVS_DP_ATTR_UPCALL_PID", procarray])
         msg["attrs"].append(["OVS_DP_ATTR_USER_FEATURES", dpfeatures])
@@ -2376,18 +2368,22 @@ class OvsVport(GenericNetlinkSocket):
                 ("tx_dropped", "=Q"),
             )
 
+    @staticmethod
     def type_to_str(vport_type):
         if vport_type == OvsVport.OVS_VPORT_TYPE_NETDEV:
             return "netdev"
-        elif vport_type == OvsVport.OVS_VPORT_TYPE_INTERNAL:
+        if vport_type == OvsVport.OVS_VPORT_TYPE_INTERNAL:
             return "internal"
+
         raise ValueError(f"Unknown vport type:{int(vport_type)}")
 
+    @staticmethod
     def str_to_type(vport_type):
         if vport_type in ["netdev", "gre", "vxlan", "geneve"]:
             return OvsVport.OVS_VPORT_TYPE_NETDEV
-        elif vport_type == "internal":
+        if vport_type == "internal":
             return OvsVport.OVS_VPORT_TYPE_INTERNAL
+
         raise ValueError(f"Unknown vport type: '{vport_type}'")
 
     def __init__(self, packet=OvsPacket()):
@@ -2482,7 +2478,7 @@ class OvsVport(GenericNetlinkSocket):
         msg["dpifindex"] = dpindex
         msg["attrs"].append(["OVS_VPORT_ATTR_NAME", vport_ifname])
 
-        if p == None:
+        if p is None:
             p = self.upcall_packet
         else:
             self.upcall_packet = p
@@ -3068,7 +3064,7 @@ def main(argv):
             return 1
         rep = ovsflow.dump(rep["dpifindex"])
         for flow in rep:
-            print(flow.dpstr(True if args.verbose > 0 else False))
+            print(flow.dpstr(args.verbose > 0))
     elif hasattr(args, "flbr"):
         rep = ovsdp.info(args.flbr, 0)
         if rep is None:
-- 
2.55.0


^ permalink raw reply related	[flat|nested] 8+ messages in thread

* [PATCH net-next v3 3/4] selftests: openvswitch: add missing docstrings in ovs-dpctl.py
  2026-09-08 17:18 [PATCH net-next v3 0/4] selftests: openvswitch: pylint cleanup of ovs-dpctl.py Minxi Hou
  2026-09-08 17:18 ` [PATCH net-next v3 1/4] selftests: openvswitch: convert %-formatting to f-strings Minxi Hou
  2026-09-08 17:18 ` [PATCH net-next v3 2/4] selftests: openvswitch: fix misc pylint warnings in ovs-dpctl.py Minxi Hou
@ 2026-09-08 17:18 ` Minxi Hou
  2026-09-10 17:22   ` netdev-bot+sashiko
  2026-09-08 17:18 ` [PATCH net-next v3 4/4] selftests: openvswitch: suppress pylint complexity warnings Minxi Hou
  3 siblings, 1 reply; 8+ messages in thread
From: Minxi Hou @ 2026-09-08 17:18 UTC (permalink / raw)
  To: netdev
  Cc: aconole, echaudro, i.maximets, davem, edumazet, kuba, pabeni,
	horms, shuah, dev, linux-kselftest, Minxi Hou

Add one-line docstrings to the module and all classes and methods
that pylint flags with C0114, C0115, and C0116 (87 instances).
Each docstring describes the command or attribute group the code
actually implements.

Signed-off-by: Minxi Hou <houminxi@gmail.com>
---
 .../selftests/net/openvswitch/ovs-dpctl.py    | 87 +++++++++++++++++++
 1 file changed, 87 insertions(+)

diff --git a/tools/testing/selftests/net/openvswitch/ovs-dpctl.py b/tools/testing/selftests/net/openvswitch/ovs-dpctl.py
index 502e6eb4c4b4a..dc88cb1b20c13 100644
--- a/tools/testing/selftests/net/openvswitch/ovs-dpctl.py
+++ b/tools/testing/selftests/net/openvswitch/ovs-dpctl.py
@@ -1,5 +1,6 @@
 #!/usr/bin/env python3
 # SPDX-License-Identifier: GPL-2.0
+"""OVS datapath control utility for kernel selftests."""
 
 # Controls the openvswitch module.  Part of the kselftest suite, but
 # can be used for some diagnostic purpose as well.
@@ -64,11 +65,13 @@ OVS_FLOW_CMD_SET = 4
 UINT32_MAX = 0xFFFFFFFF
 
 def macstr(mac):
+    """Format MAC address bytes as colon-separated hex string."""
     outstr = ":".join([f"{i:02X}" for i in mac])
     return outstr
 
 
 def strcspn(str1, str2):
+    """Return index of first char in str1 that is in str2."""
     tot = 0
     for char in str1:
         if str2.find(char) != -1:
@@ -78,6 +81,7 @@ def strcspn(str1, str2):
 
 
 def strspn(str1, str2):
+    """Return index of first char in str1 that is not in str2."""
     tot = 0
     for char in str1:
         if str2.find(char) == -1:
@@ -87,6 +91,7 @@ def strspn(str1, str2):
 
 
 def intparse(statestr, defmask="0xffffffff"):
+    """Parse an integer with optional mask from a state string."""
     totalparse = strspn(statestr, "0123456789abcdefABCDEFx/")
     # scan until "/"
     count = strspn(statestr, "x0123456789abcdefABCDEF")
@@ -107,6 +112,7 @@ def intparse(statestr, defmask="0xffffffff"):
 
 
 def parse_flags(flag_str, flag_vals):
+    """Parse a flags string into bit and mask values."""
     bitResult = 0
     maskResult = 0
 
@@ -158,6 +164,7 @@ def parse_flags(flag_str, flag_vals):
 
 
 def parse_ct_state(statestr):
+    """Parse a conntrack state string into flag bits."""
     ct_flags = {
         "new": 1 << 0,
         "est": 1 << 1,
@@ -173,6 +180,7 @@ def parse_ct_state(statestr):
 
 
 def convert_mac(data):
+    """Convert a MAC address string with optional mask to a bytes pair."""
     def to_bytes(mac):
         mac_split = mac.split(":")
         ret = bytearray([int(i, 16) for i in mac_split])
@@ -188,6 +196,7 @@ def convert_mac(data):
     return to_bytes(mac_str), to_bytes(mask_str)
 
 def convert_ipv4(data):
+    """Convert an IPv4 address/mask string to an integer tuple."""
     ip, _, mask = data.partition('/')
 
     if not ip:
@@ -200,6 +209,7 @@ def convert_ipv4(data):
     return int(ipaddress.IPv4Address(ip)), int(ipaddress.IPv4Address(mask))
 
 def convert_ipv6(data):
+    """Convert an IPv6 address/mask string to a packed bytes tuple."""
     ip, _, mask = data.partition('/')
 
     if not ip:
@@ -212,6 +222,7 @@ def convert_ipv6(data):
     return ipaddress.IPv6Address(ip).packed, ipaddress.IPv6Address(mask).packed
 
 def convert_int(size):
+    """Return a parser for fixed-width integers with optional mask."""
     def convert_int_sized(data):
         value, _, mask = data.partition('/')
 
@@ -224,6 +235,7 @@ def convert_int(size):
     return convert_int_sized
 
 def parse_starts_block(block_str, scanstr, returnskipped, scanregex=False):
+    """Check for and optionally consume a prefix in block_str."""
     if scanregex:
         m = re.search(scanstr, block_str)
         if m is None:
@@ -250,6 +262,7 @@ def parse_starts_block(block_str, scanstr, returnskipped, scanregex=False):
 def parse_extract_field(
     block_str, fieldstr, scanfmt, convert, masked=False, defval=None
 ):
+    """Extract and convert one field from a flow string."""
     if fieldstr and not block_str.startswith(fieldstr):
         return block_str, defval
 
@@ -349,6 +362,7 @@ def parse_attrs(actstr, attr_desc):
 
 
 class ovs_dp_msg(genlmsg):
+    """Datapath generic netlink message with OVS version and dpifindex."""
     # include the OVS version
     # We need a custom header rather than just being able to rely on
     # genlmsg because fields ends up not expressing everything correctly
@@ -357,6 +371,7 @@ class ovs_dp_msg(genlmsg):
 
 
 class ovsactions(nla):
+    """OVS action attribute list (OVS_ACTION_ATTR_*)."""
     nla_flags = NLA_F_NESTED
 
     nla_map = (
@@ -399,6 +414,7 @@ class ovsactions(nla):
         )
 
     class psample(nla):
+        """psample action attributes (OVS_PSAMPLE_ATTR_*)."""
         nla_flags = NLA_F_NESTED
 
         nla_map = (
@@ -408,6 +424,7 @@ class ovsactions(nla):
         )
 
         def dpstr(self, more=False):
+            """Format the psample action as a dpctl string."""
             args = f"group={int(self.get_attr('OVS_PSAMPLE_ATTR_GROUP'))}"
 
             cookie = self.get_attr("OVS_PSAMPLE_ATTR_COOKIE")
@@ -417,6 +434,7 @@ class ovsactions(nla):
             return f"psample({args})"
 
         def parse(self, actstr):
+            """Parse a psample() action string."""
             desc = (
                 ("group", "OVS_PSAMPLE_ATTR_GROUP", int),
                 ("cookie", "OVS_PSAMPLE_ATTR_COOKIE",
@@ -431,9 +449,11 @@ class ovsactions(nla):
             return actstr
 
     class push_vlan(nla):
+        """push_vlan action attributes (vlan_tpid/vlan_tci)."""
         fields = (("vlan_tpid", "!H"), ("vlan_tci", "!H"))
 
     class sample(nla):
+        """Sample action attributes (OVS_SAMPLE_ATTR_*)."""
         nla_flags = NLA_F_NESTED
 
         nla_map = (
@@ -443,6 +463,7 @@ class ovsactions(nla):
         )
 
         def dpstr(self, more=False):
+            """Format the sample action as a dpctl string."""
             args = []
 
             prob = 100 * self.get_attr(
@@ -456,6 +477,7 @@ class ovsactions(nla):
             return f"sample({','.join(args)})"
 
         def parse(self, actstr):
+            """Parse a sample() action string."""
             def parse_nested_actions(actstr):
                 subacts = ovsactions()
                 parsed_len = subacts.parse(actstr)
@@ -477,6 +499,7 @@ class ovsactions(nla):
             return actstr
 
     class ctact(nla):
+        """Conntrack action attributes (OVS_CT_ATTR_*)."""
         nla_flags = NLA_F_NESTED
 
         nla_map = (
@@ -493,6 +516,7 @@ class ovsactions(nla):
         )
 
         class natattr(nla):
+            """NAT attributes for the conntrack action (OVS_NAT_ATTR_*)."""
             nla_flags = NLA_F_NESTED
 
             nla_map = (
@@ -509,6 +533,7 @@ class ovsactions(nla):
             )
 
             def dpstr(self, more=False):
+                """Format the NAT attributes as a dpctl string."""
                 print_str = "nat("
 
                 if self.get_attr("OVS_NAT_ATTR_SRC"):
@@ -547,6 +572,7 @@ class ovsactions(nla):
                 return print_str
 
         def dpstr(self, more=False):
+            """Format the conntrack action as a dpctl string."""
             print_str = "ct("
 
             if self.get_attr("OVS_CT_ATTR_COMMIT") is not None:
@@ -568,6 +594,7 @@ class ovsactions(nla):
             return print_str
 
     class userspace(nla):
+        """Userspace action attributes (OVS_USERSPACE_ATTR_*)."""
         nla_flags = NLA_F_NESTED
 
         nla_map = (
@@ -578,6 +605,7 @@ class ovsactions(nla):
         )
 
         def dpstr(self, more=False):
+            """Format the userspace action as a dpctl string."""
             print_str = "userspace("
             if self.get_attr("OVS_USERSPACE_ATTR_PID") is not None:
                 print_str += f"pid={int(self.get_attr('OVS_USERSPACE_ATTR_PID'))},"
@@ -593,6 +621,7 @@ class ovsactions(nla):
             return print_str
 
         def parse(self, actstr):
+            """Parse a userspace() action string."""
             attrs_desc = (
                 ("pid", "OVS_USERSPACE_ATTR_PID", int),
                 ("userdata", "OVS_USERSPACE_ATTR_USERDATA",
@@ -607,6 +636,7 @@ class ovsactions(nla):
             return actstr
 
     def dpstr(self, more=False):
+        """Format the action list as a dpctl string."""
         print_str = ""
 
         for field in self["attrs"]:
@@ -675,6 +705,7 @@ class ovsactions(nla):
         return print_str
 
     def parse(self, actstr):
+        """Parse a dpctl action string into attributes."""
         totallen = len(actstr)
         while len(actstr) != 0:
             parsed = False
@@ -971,6 +1002,7 @@ ovsactions.dec_ttl.actions = ovsactions
 
 
 class ovskey(nla):
+    """OVS flow key attributes (OVS_KEY_ATTR_*)."""
     nla_flags = NLA_F_NESTED
     nla_map = (
         ("OVS_KEY_ATTR_UNSPEC", "none"),
@@ -1009,6 +1041,7 @@ class ovskey(nla):
     )
 
     class ovs_key_proto(nla):
+        """Base class for protocol-specific flow key fields."""
         fields = (
             ("src", "!H"),
             ("dst", "!H"),
@@ -1041,6 +1074,7 @@ class ovskey(nla):
             )
 
         def parse(self, flowstr, typeInst):
+            """Parse this protocol's key and mask fields from a flow string."""
             if not flowstr.startswith(self.proto_str):
                 return None, None
 
@@ -1082,6 +1116,7 @@ class ovskey(nla):
             return flowstr, k, m
 
         def dpstr(self, masked=None, more=False):
+            """Format this protocol's key fields as a dpctl string."""
             outstr = f"{self.proto_str}("
             first = False
             for f in self.fields_map:
@@ -1110,6 +1145,7 @@ class ovskey(nla):
             return outstr
 
     class ethaddr(ovs_key_proto):
+        """Ethernet address flow key (OVS_KEY_ATTR_ETHERNET)."""
         fields = (
             ("src", "!6s"),
             ("dst", "!6s"),
@@ -1151,6 +1187,7 @@ class ovskey(nla):
             )
 
     class ovs_key_ipv4(ovs_key_proto):
+        """IPv4 flow key (OVS_KEY_ATTR_IPV4)."""
         fields = (
             ("src", "!I"),
             ("dst", "!I"),
@@ -1204,6 +1241,7 @@ class ovskey(nla):
             )
 
     class ovs_key_ipv6(ovs_key_proto):
+        """IPv6 flow key (OVS_KEY_ATTR_IPV6)."""
         fields = (
             ("src", "!16s"),
             ("dst", "!16s"),
@@ -1260,6 +1298,7 @@ class ovskey(nla):
             )
 
     class ovs_key_tcp(ovs_key_proto):
+        """TCP port flow key (OVS_KEY_ATTR_TCP)."""
         def __init__(
             self,
             data=None,
@@ -1279,6 +1318,7 @@ class ovskey(nla):
             )
 
     class ovs_key_udp(ovs_key_proto):
+        """UDP port flow key (OVS_KEY_ATTR_UDP)."""
         def __init__(
             self,
             data=None,
@@ -1298,6 +1338,7 @@ class ovskey(nla):
             )
 
     class ovs_key_sctp(ovs_key_proto):
+        """SCTP port flow key (OVS_KEY_ATTR_SCTP)."""
         def __init__(
             self,
             data=None,
@@ -1317,6 +1358,7 @@ class ovskey(nla):
             )
 
     class ovs_key_icmp(ovs_key_proto):
+        """ICMP flow key (OVS_KEY_ATTR_ICMP)."""
         fields = (
             ("type", "B"),
             ("code", "B"),
@@ -1348,6 +1390,7 @@ class ovskey(nla):
             )
 
     class ovs_key_icmpv6(ovs_key_icmp):
+        """ICMPv6 flow key (OVS_KEY_ATTR_ICMPV6)."""
         def __init__(
             self,
             data=None,
@@ -1367,6 +1410,7 @@ class ovskey(nla):
             )
 
     class ovs_key_arp(ovs_key_proto):
+        """ARP flow key (OVS_KEY_ATTR_ARP)."""
         fields = (
             ("sip", "!I"),
             ("tip", "!I"),
@@ -1427,6 +1471,7 @@ class ovskey(nla):
             )
 
     class ovs_key_nd(ovs_key_proto):
+        """IPv6 Neighbor Discovery flow key (OVS_KEY_ATTR_ND)."""
         fields = (
             ("target", "!16s"),
             ("sll", "!6s"),
@@ -1463,6 +1508,7 @@ class ovskey(nla):
             )
 
     class ovs_key_ct_tuple_ipv4(ovs_key_proto):
+        """IPv4 conntrack tuple key (OVS_KEY_ATTR_CT_TUPLE_IPV4)."""
         fields = (
             ("src", "!I"),
             ("dst", "!I"),
@@ -1510,6 +1556,7 @@ class ovskey(nla):
             )
 
     class ovs_key_ct_tuple_ipv6(nla):
+        """IPv6 conntrack tuple key (OVS_KEY_ATTR_CT_TUPLE_IPV6)."""
         fields = (
             ("src", "!16s"),
             ("dst", "!16s"),
@@ -1555,6 +1602,7 @@ class ovskey(nla):
             )
 
     class ovs_key_tunnel(nla):
+        """Tunnel flow key attributes (OVS_TUNNEL_KEY_ATTR_*)."""
         nla_flags = NLA_F_NESTED
 
         nla_map = (
@@ -1578,6 +1626,7 @@ class ovskey(nla):
         )
 
         def parse(self, flowstr, mask=None):
+            """Parse a tunnel() key string."""
             if not flowstr.startswith("tunnel("):
                 return None, None
 
@@ -1670,6 +1719,7 @@ class ovskey(nla):
             return flowstr, k, mask
 
         def dpstr(self, mask=None, more=False):
+            """Format the tunnel key as a dpctl string."""
             print_str = "tunnel("
 
             flagsattrs = []
@@ -1712,6 +1762,7 @@ class ovskey(nla):
             return print_str
 
     class ovs_key_mpls(nla):
+        """MPLS flow key (OVS_KEY_ATTR_MPLS)."""
         fields = (("lse", ">I"),)
 
     # 802.1Q CFI (Canonical Format Indicator) bit, always set for Ethernet
@@ -1901,6 +1952,7 @@ class ovskey(nla):
         return flowstr, inner_key, inner_mask
 
     def parse(self, flowstr, mask=None):
+        """Parse a flow key string into key and mask attributes."""
         for field in (
             ("OVS_KEY_ATTR_PRIORITY", "skb_priority", intparse),
             ("OVS_KEY_ATTR_SKB_MARK", "skb_mark", intparse),
@@ -1997,6 +2049,7 @@ class ovskey(nla):
         return flowstr
 
     def dpstr(self, mask=None, more=False):
+        """Format the flow key as a dpctl string."""
         print_str = ""
 
         for field in (
@@ -2166,11 +2219,13 @@ class encap_ovskey(ovskey):
 
 
 class OvsPacket(GenericNetlinkSocket):
+    """OVS packet command socket (miss/action/execute upcalls)."""
     OVS_PACKET_CMD_MISS = 1  # Flow table miss
     OVS_PACKET_CMD_ACTION = 2  # USERSPACE action
     OVS_PACKET_CMD_EXECUTE = 3  # Apply actions to packet
 
     class ovs_packet_msg(ovs_dp_msg):
+        """Packet command message attributes (OVS_PACKET_ATTR_*)."""
         nla_map = (
             ("OVS_PACKET_ATTR_UNSPEC", "none"),
             ("OVS_PACKET_ATTR_PACKET", "array(uint8)"),
@@ -2191,6 +2246,7 @@ class OvsPacket(GenericNetlinkSocket):
         self.bind(OVS_PACKET_FAMILY, OvsPacket.ovs_packet_msg)
 
     def upcall_handler(self, up=None):
+        """Listen for packet upcalls and dispatch them to the handler."""
         print("listening on upcall packet handler:", self.epid)
         while True:
             try:
@@ -2211,6 +2267,7 @@ class OvsPacket(GenericNetlinkSocket):
 
 
 class OvsDatapath(GenericNetlinkSocket):
+    """OVS datapath command socket (create/destroy/lookup)."""
     OVS_DP_F_VPORT_PIDS = 1 << 1
     OVS_DP_F_DISPATCH_UPCALL_PER_CPU = 1 << 3
 
@@ -2232,6 +2289,7 @@ class OvsDatapath(GenericNetlinkSocket):
         )
 
         class dpstats(nla):
+            """Datapath statistics (OVS_DP_ATTR_STATS)."""
             fields = (
                 ("hit", "=Q"),
                 ("missed", "=Q"),
@@ -2240,6 +2298,7 @@ class OvsDatapath(GenericNetlinkSocket):
             )
 
         class megaflowstats(nla):
+            """Datapath megaflow statistics (OVS_DP_ATTR_MEGAFLOW_STATS)."""
             fields = (
                 ("mask_hit", "=Q"),
                 ("masks", "=I"),
@@ -2253,6 +2312,7 @@ class OvsDatapath(GenericNetlinkSocket):
         self.bind(OVS_DATAPATH_FAMILY, OvsDatapath.dp_cmd_msg)
 
     def info(self, dpname, ifindex=0):
+        """Look up a datapath by name."""
         msg = OvsDatapath.dp_cmd_msg()
         msg["cmd"] = OVS_DP_CMD_GET
         msg["version"] = OVS_DATAPATH_VERSION
@@ -2276,6 +2336,7 @@ class OvsDatapath(GenericNetlinkSocket):
     def create(
         self, dpname, shouldUpcall=False, versionStr=None, p=OvsPacket()
     ):
+        """Create a new datapath."""
         msg = OvsDatapath.dp_cmd_msg()
         msg["cmd"] = OVS_DP_CMD_NEW
         if versionStr is None:
@@ -2317,6 +2378,7 @@ class OvsDatapath(GenericNetlinkSocket):
         return reply
 
     def destroy(self, dpname):
+        """Destroy a datapath."""
         msg = OvsDatapath.dp_cmd_msg()
         msg["cmd"] = OVS_DP_CMD_DEL
         msg["version"] = OVS_DATAPATH_VERSION
@@ -2339,10 +2401,12 @@ class OvsDatapath(GenericNetlinkSocket):
 
 
 class OvsVport(GenericNetlinkSocket):
+    """OVS vport command socket (attach/detach/lookup)."""
     OVS_VPORT_TYPE_NETDEV = 1
     OVS_VPORT_TYPE_INTERNAL = 2
 
     class ovs_vport_msg(ovs_dp_msg):
+        """Vport command message attributes (OVS_VPORT_ATTR_*)."""
         nla_map = (
             ("OVS_VPORT_ATTR_UNSPEC", "none"),
             ("OVS_VPORT_ATTR_PORT_NO", "uint32"),
@@ -2357,6 +2421,7 @@ class OvsVport(GenericNetlinkSocket):
         )
 
         class vportstats(nla):
+            """Vport statistics (OVS_VPORT_ATTR_STATS)."""
             fields = (
                 ("rx_packets", "=Q"),
                 ("tx_packets", "=Q"),
@@ -2370,6 +2435,7 @@ class OvsVport(GenericNetlinkSocket):
 
     @staticmethod
     def type_to_str(vport_type):
+        """Convert a vport type constant to its string name."""
         if vport_type == OvsVport.OVS_VPORT_TYPE_NETDEV:
             return "netdev"
         if vport_type == OvsVport.OVS_VPORT_TYPE_INTERNAL:
@@ -2379,6 +2445,7 @@ class OvsVport(GenericNetlinkSocket):
 
     @staticmethod
     def str_to_type(vport_type):
+        """Convert a vport type string to its constant."""
         if vport_type in ["netdev", "gre", "vxlan", "geneve"]:
             return OvsVport.OVS_VPORT_TYPE_NETDEV
         if vport_type == "internal":
@@ -2392,6 +2459,7 @@ class OvsVport(GenericNetlinkSocket):
         self.upcall_packet = packet
 
     def info(self, vport_name, dpifindex=0, portno=None):
+        """Get info about a vport."""
         msg = OvsVport.ovs_vport_msg()
 
         msg["cmd"] = OVS_VPORT_CMD_GET
@@ -2417,6 +2485,7 @@ class OvsVport(GenericNetlinkSocket):
         return reply
 
     def attach(self, dpindex, vport_ifname, ptype, dport):
+        """Create a vport and attach it to a datapath."""
         msg = OvsVport.ovs_vport_msg()
 
         msg["cmd"] = OVS_VPORT_CMD_NEW
@@ -2470,6 +2539,7 @@ class OvsVport(GenericNetlinkSocket):
         return reply
 
     def reset_upcall(self, dpindex, vport_ifname, p=None):
+        """Reset a vport's upcall pid."""
         msg = OvsVport.ovs_vport_msg()
 
         msg["cmd"] = OVS_VPORT_CMD_SET
@@ -2495,6 +2565,7 @@ class OvsVport(GenericNetlinkSocket):
         return reply
 
     def detach(self, dpindex, vport_ifname):
+        """Remove a vport from a datapath."""
         msg = OvsVport.ovs_vport_msg()
 
         msg["cmd"] = OVS_VPORT_CMD_DEL
@@ -2516,11 +2587,14 @@ class OvsVport(GenericNetlinkSocket):
         return reply
 
     def upcall_handler(self, handler=None):
+        """Delegate upcall handling to the packet socket."""
         self.upcall_packet.upcall_handler(handler)
 
 
 class OvsFlow(GenericNetlinkSocket):
+    """OVS flow command socket (add/modify/delete/dump flows)."""
     class ovs_flow_msg(ovs_dp_msg):
+        """Flow command message attributes (OVS_FLOW_ATTR_*)."""
         nla_map = (
             ("OVS_FLOW_ATTR_UNSPEC", "none"),
             ("OVS_FLOW_ATTR_KEY", "ovskey"),
@@ -2536,12 +2610,14 @@ class OvsFlow(GenericNetlinkSocket):
         )
 
         class flowstats(nla):
+            """Flow statistics (OVS_FLOW_ATTR_STATS)."""
             fields = (
                 ("packets", "=Q"),
                 ("bytes", "=Q"),
             )
 
         def dpstr(self, more=False):
+            """Format the flow message as a dpctl string."""
             ufid = self.get_attr("OVS_FLOW_ATTR_UFID")
             ufid_str = ""
             if ufid is not None:
@@ -2608,6 +2684,7 @@ class OvsFlow(GenericNetlinkSocket):
             return print_str
 
         def parse(self, flowstr, actstr, dpidx=0):
+            """Parse flow and action strings into a flow message."""
             OVS_UFID_F_OMIT_KEY = 1 << 0
             OVS_UFID_F_OMIT_MASK = 1 << 1
             OVS_UFID_F_OMIT_ACTIONS = 1 << 2
@@ -2772,6 +2849,7 @@ class OvsFlow(GenericNetlinkSocket):
         return rep
 
     def miss(self, packetmsg):
+        """Handle a flow-table miss upcall."""
         seq = packetmsg["header"]["sequence_number"]
         keystr = "(none)"
         key_field = packetmsg.get_attr("OVS_PACKET_ATTR_KEY")
@@ -2784,13 +2862,16 @@ class OvsFlow(GenericNetlinkSocket):
         print(f"MISS upcall[{int(seq)}/{pktpres}]: {keystr}", flush=True)
 
     def execute(self, packetmsg):
+        """Handle a userspace execute upcall."""
         print("userspace execute command", flush=True)
 
     def action(self, packetmsg):
+        """Handle a userspace action upcall."""
         print("userspace action command", flush=True)
 
 
 class psample_sample(genlmsg):
+    """psample sample event message (PSAMPLE_ATTR_*)."""
     nla_map = (
         ("PSAMPLE_ATTR_IIFINDEX", "none"),
         ("PSAMPLE_ATTR_OIFINDEX", "none"),
@@ -2811,6 +2892,7 @@ class psample_sample(genlmsg):
     )
 
     def dpstr(self):
+        """Format the psample event as a string."""
         fields = []
         data = ""
         for (attr, value) in self["attrs"]:
@@ -2828,6 +2910,7 @@ class psample_sample(genlmsg):
 
 
 class psample_msg(Marshal):
+    """psample generic netlink message marshaller."""
     PSAMPLE_CMD_SAMPLE = 0
     PSAMPLE_CMD_GET_GROUP = 1
     PSAMPLE_CMD_NEW_GROUP = 2
@@ -2837,11 +2920,13 @@ class psample_msg(Marshal):
 
 
 class PsampleEvent(EventSocket):
+    """Socket listening for psample multicast events."""
     genl_family = "psample"
     mcast_groups = ["packets"]
     marshal_class = psample_msg
 
     def read_samples(self):
+        """Print psample events as they arrive."""
         print("listening for psample events", flush=True)
         while True:
             try:
@@ -2852,6 +2937,7 @@ class PsampleEvent(EventSocket):
 
 
 def print_ovsdp_full(dp_lookup_rep, ifindex, ndb=NDB(), vpl=OvsVport()):
+    """Print full datapath state: stats, vports, and flows."""
     dp_name = dp_lookup_rep.get_attr("OVS_DP_ATTR_NAME")
     base_stats = dp_lookup_rep.get_attr("OVS_DP_ATTR_STATS")
     megaflow_stats = dp_lookup_rep.get_attr("OVS_DP_ATTR_MEGAFLOW_STATS")
@@ -2887,6 +2973,7 @@ def print_ovsdp_full(dp_lookup_rep, ifindex, ndb=NDB(), vpl=OvsVport()):
 
 
 def main(argv):
+    """Entry point: parse arguments and run the ovs-dpctl command."""
     nlmsg_atoms.encap_ovskey = encap_ovskey
     nlmsg_atoms.ovskey = ovskey
     nlmsg_atoms.ovsactions = ovsactions
-- 
2.55.0


^ permalink raw reply related	[flat|nested] 8+ messages in thread

* [PATCH net-next v3 4/4] selftests: openvswitch: suppress pylint complexity warnings
  2026-09-08 17:18 [PATCH net-next v3 0/4] selftests: openvswitch: pylint cleanup of ovs-dpctl.py Minxi Hou
                   ` (2 preceding siblings ...)
  2026-09-08 17:18 ` [PATCH net-next v3 3/4] selftests: openvswitch: add missing docstrings " Minxi Hou
@ 2026-09-08 17:18 ` Minxi Hou
  2026-09-10 17:22   ` netdev-bot+sashiko
  3 siblings, 1 reply; 8+ messages in thread
From: Minxi Hou @ 2026-09-08 17:18 UTC (permalink / raw)
  To: netdev
  Cc: aconole, echaudro, i.maximets, davem, edumazet, kuba, pabeni,
	horms, shuah, dev, linux-kselftest, Minxi Hou

The module-level disables cover complexity and structural warnings
that match how the tool is written (large netlink message classes
with long attribute tables) and are not worth restructuring a
selftest helper for.

Signed-off-by: Minxi Hou <houminxi@gmail.com>
---
 tools/testing/selftests/net/openvswitch/ovs-dpctl.py | 8 ++++++++
 1 file changed, 8 insertions(+)

diff --git a/tools/testing/selftests/net/openvswitch/ovs-dpctl.py b/tools/testing/selftests/net/openvswitch/ovs-dpctl.py
index dc88cb1b20c13..7ea0d52044011 100644
--- a/tools/testing/selftests/net/openvswitch/ovs-dpctl.py
+++ b/tools/testing/selftests/net/openvswitch/ovs-dpctl.py
@@ -1,6 +1,14 @@
 #!/usr/bin/env python3
 # SPDX-License-Identifier: GPL-2.0
 """OVS datapath control utility for kernel selftests."""
+# pylint: disable=invalid-name,too-many-lines
+# pylint: disable=too-many-ancestors,too-many-arguments
+# pylint: disable=too-many-positional-arguments,too-many-branches
+# pylint: disable=too-many-locals,too-many-statements
+# pylint: disable=too-many-return-statements,too-many-nested-blocks
+# pylint: disable=unused-argument,broad-exception-caught
+# pylint: disable=no-member,not-callable
+# pylint: disable=non-parent-init-called,super-init-not-called
 
 # Controls the openvswitch module.  Part of the kselftest suite, but
 # can be used for some diagnostic purpose as well.
-- 
2.55.0


^ permalink raw reply related	[flat|nested] 8+ messages in thread

* Re: [PATCH net-next v3 3/4] selftests: openvswitch: add missing docstrings in ovs-dpctl.py
  2026-09-08 17:18 ` [PATCH net-next v3 3/4] selftests: openvswitch: add missing docstrings " Minxi Hou
@ 2026-09-10 17:22   ` netdev-bot+sashiko
  2026-09-10 17:44     ` Aaron Conole
  0 siblings, 1 reply; 8+ messages in thread
From: netdev-bot+sashiko @ 2026-09-10 17:22 UTC (permalink / raw)
  To: houminxi
  Cc: netdev, aconole, echaudro, i.maximets, davem, edumazet, kuba,
	pabeni, horms, shuah, dev, linux-kselftest

Thank you for your contribution! Sashiko AI review found 1 potential
issue(s) to consider:

Critical: 0 · High: 0 · Medium: 0 · Low: 1

- [Low] The docstring added to print_ovsdp_full()
  (tools/testing/selftests/net/openvswitch/ovs-dpctl.py line 2940)…

--

selftests: openvswitch: add missing docstrings in ovs-dpctl.py

This adds one-line docstrings to the module, classes and methods in the
openvswitch selftest helper ovs-dpctl.py to silence pylint C0114, C0115
and C0116. No code behaviour is changed.

> diff --git a/tools/testing/selftests/net/openvswitch/ovs-dpctl.py b/tools/testing/selftests/net/openvswitch/ovs-dpctl.py
> index 502e6eb4c4b4..dc88cb1b20c1 100644
> --- a/tools/testing/selftests/net/openvswitch/ovs-dpctl.py
> +++ b/tools/testing/selftests/net/openvswitch/ovs-dpctl.py

[ ... ]

> @@ -2887,6 +2973,7 @@ class PsampleEvent(EventSocket):
>  
>  
>  def print_ovsdp_full(dp_lookup_rep, ifindex, ndb=NDB(), vpl=OvsVport()):
> +    """Print full datapath state: stats, vports, and flows."""
>  	  dp_name = dp_lookup_rep.get_attr("OVS_DP_ATTR_NAME")
>  	  base_stats = dp_lookup_rep.get_attr("OVS_DP_ATTR_STATS")
>  	  megaflow_stats = dp_lookup_rep.get_attr("OVS_DP_ATTR_MEGAFLOW_STATS")

[Severity: Low]
Does print_ovsdp_full() actually print flows?  The body only emits the
datapath name, the values from OVS_DP_ATTR_STATS,
OVS_DP_ATTR_MEGAFLOW_STATS, OVS_DP_ATTR_MASKS_CACHE_SIZE and
OVS_DP_ATTR_USER_FEATURES, then one line per vport:

    print(f"  flows:{int(base_stats['flows'])}")
    ...
    # port print out
    for iface in ndb.interfaces:
        rep = vpl.info(iface.ifname, ifindex)
        if rep is not None:
            print(
                f"  port {int(rep.get_attr('OVS_VPORT_ATTR_PORT_NO'))}: "
                ...

The only flow-related output is the flows:<n> counter that comes from
OVS_DP_ATTR_STATS, which is already covered by "stats".

Flow enumeration lives in OvsFlow.dump(), and main() calls that only for
the dump subcommand, not on the show path that reaches
print_ovsdp_full().

Would something like "Print datapath statistics and attached vports." be
a more accurate one-liner here?

The commit message says "Each docstring describes the command or
attribute group the code actually implements", so this one looks like it
overstates the output.

-- 
Sashiko AI review · https://netdev-ai.bots.linux.dev/sashiko/#/patchset/20260908171859.1967327-1-houminxi%40gmail.com

^ permalink raw reply	[flat|nested] 8+ messages in thread

* Re: [PATCH net-next v3 4/4] selftests: openvswitch: suppress pylint complexity warnings
  2026-09-08 17:18 ` [PATCH net-next v3 4/4] selftests: openvswitch: suppress pylint complexity warnings Minxi Hou
@ 2026-09-10 17:22   ` netdev-bot+sashiko
  0 siblings, 0 replies; 8+ messages in thread
From: netdev-bot+sashiko @ 2026-09-10 17:22 UTC (permalink / raw)
  To: houminxi
  Cc: netdev, aconole, echaudro, i.maximets, davem, edumazet, kuba,
	pabeni, horms, shuah, dev, linux-kselftest

Thank you for your contribution! Sashiko AI review found 1 potential
issue(s) to consider:

Critical: 0 · High: 0 · Medium: 1 · Low: 0 (1)

- [Medium] Commit message mischaracterizes the change: the subject and
  body claim the patch suppresses "complexity and structural warnings"…

Pre-existing issues:
- [Low] Latent inheritance-declaration inconsistency now permanently
  masked: `ovskey.ovs_key_ct_tuple_ipv6` is declared `class…

--

selftests: openvswitch: suppress pylint complexity warnings

This adds a block of module-level pylint disable directives to the top of
tools/testing/selftests/net/openvswitch/ovs-dpctl.py.  The commit message
describes them as complexity and structural suppressions matching the large
netlink message classes in the tool.

> diff --git a/tools/testing/selftests/net/openvswitch/ovs-dpctl.py b/tools/testing/selftests/net/openvswitch/ovs-dpctl.py
> index dc88cb1b20c1..7ea0d5204401 100644
> --- a/tools/testing/selftests/net/openvswitch/ovs-dpctl.py
> +++ b/tools/testing/selftests/net/openvswitch/ovs-dpctl.py
> @@ -1,6 +1,14 @@
>  #!/usr/bin/env python3
>  # SPDX-License-Identifier: GPL-2.0
>  """OVS datapath control utility for kernel selftests."""
> +# pylint: disable=invalid-name,too-many-lines
> +# pylint: disable=too-many-ancestors,too-many-arguments
> +# pylint: disable=too-many-positional-arguments,too-many-branches
> +# pylint: disable=too-many-locals,too-many-statements
> +# pylint: disable=too-many-return-statements,too-many-nested-blocks
> +# pylint: disable=unused-argument,broad-exception-caught
> +# pylint: disable=no-member,not-callable

[Severity: Medium]
Does the commit message match what these lines actually do?  The subject says
"suppress pylint complexity warnings" and the body says the disables "cover
complexity and structural warnings", but only the too-many-* entries are
complexity checks.

no-member (E1101) and not-callable (E1102) are pylint error-category
diagnostics, and invalid-name (C0103), unused-argument (W0613),
broad-exception-caught (W0718), non-parent-init-called (W0233) and
super-init-not-called (W0231) are unrelated convention/warning checks.

Because these are module-scope directives they apply to the whole file
(roughly 3200 lines), so a misspelled attribute access or a call on a
non-callable anywhere in ovs-dpctl.py can no longer be reported.  This lands
alongside other patches that rewrite format strings and control flow in the
same file.

Could the message spell out that error-class checks are being turned off
file-wide, or could no-member/not-callable be narrowed to the specific
classes that need them?

Also, invalid-name is now disabled module-wide, which makes the pre-existing
inline suppression in ovsactions dead code:

    class dec_ttl(nla):  # pylint: disable=invalid-name

Should that inline disable be dropped in the same patch so there is only one
suppression scope in the file?

> +# pylint: disable=non-parent-init-called,super-init-not-called

[Severity: Low]
This isn't a bug introduced by this patch, but disabling
non-parent-init-called module-wide removes the only automated signal for an
existing inconsistency in ovskey.

ovskey.ovs_key_ct_tuple_ipv6 is declared with nla as its base:

    class ovs_key_ct_tuple_ipv6(nla):
        """IPv6 conntrack tuple key (OVS_KEY_ATTR_CT_TUPLE_IPV6)."""

but its __init__ initializes through a class that is not in its ancestry:

            ovskey.ovs_key_proto.__init__(
                self,
                "ct_tuple6",
                ...

Its ABI peer does derive from ovs_key_proto:

    class ovs_key_ct_tuple_ipv4(ovs_key_proto):

Nothing appears to misbehave today: ovs_key_proto.__init__ only sets
self.proto_str and forwards to nla.__init__, which is a real ancestor, and
neither ct_tuple4 nor ct_tuple6 appears in the ovskey.parse() or
ovskey.dpstr() dispatch tables, so the ovs_key_proto.parse()/dpstr() methods
that ovs_key_ct_tuple_ipv6 does not inherit are never called.

Would it be worth fixing the declared base class to ovs_key_proto rather
than silencing the check that flags it?

-- 
Sashiko AI review · https://netdev-ai.bots.linux.dev/sashiko/#/patchset/20260908171859.1967327-1-houminxi%40gmail.com

^ permalink raw reply	[flat|nested] 8+ messages in thread

* Re: [PATCH net-next v3 3/4] selftests: openvswitch: add missing docstrings in ovs-dpctl.py
  2026-09-10 17:22   ` netdev-bot+sashiko
@ 2026-09-10 17:44     ` Aaron Conole
  0 siblings, 0 replies; 8+ messages in thread
From: Aaron Conole @ 2026-09-10 17:44 UTC (permalink / raw)
  To: netdev-bot+sashiko
  Cc: houminxi, netdev, echaudro, i.maximets, davem, edumazet, kuba,
	pabeni, horms, shuah, dev, linux-kselftest

netdev-bot+sashiko@kernel.org writes:

> Thank you for your contribution! Sashiko AI review found 1 potential
> issue(s) to consider:
>
> Critical: 0 · High: 0 · Medium: 0 · Low: 1
>
> - [Low] The docstring added to print_ovsdp_full()
>   (tools/testing/selftests/net/openvswitch/ovs-dpctl.py line 2940)…
>
> --
>
> selftests: openvswitch: add missing docstrings in ovs-dpctl.py
>
> This adds one-line docstrings to the module, classes and methods in the
> openvswitch selftest helper ovs-dpctl.py to silence pylint C0114, C0115
> and C0116. No code behaviour is changed.
>
>> diff --git a/tools/testing/selftests/net/openvswitch/ovs-dpctl.py
>> b/tools/testing/selftests/net/openvswitch/ovs-dpctl.py
>> index 502e6eb4c4b4..dc88cb1b20c1 100644
>> --- a/tools/testing/selftests/net/openvswitch/ovs-dpctl.py
>> +++ b/tools/testing/selftests/net/openvswitch/ovs-dpctl.py
>
> [ ... ]
>
>> @@ -2887,6 +2973,7 @@ class PsampleEvent(EventSocket):
>>  
>>  
>>  def print_ovsdp_full(dp_lookup_rep, ifindex, ndb=NDB(), vpl=OvsVport()):
>> +    """Print full datapath state: stats, vports, and flows."""
>>  	  dp_name = dp_lookup_rep.get_attr("OVS_DP_ATTR_NAME")
>>  	  base_stats = dp_lookup_rep.get_attr("OVS_DP_ATTR_STATS")
>>  	  megaflow_stats = dp_lookup_rep.get_attr("OVS_DP_ATTR_MEGAFLOW_STATS")
>
> [Severity: Low]
> Does print_ovsdp_full() actually print flows?  The body only emits the
> datapath name, the values from OVS_DP_ATTR_STATS,
> OVS_DP_ATTR_MEGAFLOW_STATS, OVS_DP_ATTR_MASKS_CACHE_SIZE and
> OVS_DP_ATTR_USER_FEATURES, then one line per vport:
>
>     print(f"  flows:{int(base_stats['flows'])}")
>     ...
>     # port print out
>     for iface in ndb.interfaces:
>         rep = vpl.info(iface.ifname, ifindex)
>         if rep is not None:
>             print(
>                 f"  port {int(rep.get_attr('OVS_VPORT_ATTR_PORT_NO'))}: "
>                 ...
>
> The only flow-related output is the flows:<n> counter that comes from
> OVS_DP_ATTR_STATS, which is already covered by "stats".
>
> Flow enumeration lives in OvsFlow.dump(), and main() calls that only for
> the dump subcommand, not on the show path that reaches
> print_ovsdp_full().
>
> Would something like "Print datapath statistics and attached vports." be
> a more accurate one-liner here?

I agree with this verbiage.

> The commit message says "Each docstring describes the command or
> attribute group the code actually implements", so this one looks like it
> overstates the output.


^ permalink raw reply	[flat|nested] 8+ messages in thread

end of thread, other threads:[~2026-09-10 17:44 UTC | newest]

Thread overview: 8+ messages (download: mbox.gz follow: Atom feed
-- links below jump to the message on this page --
2026-09-08 17:18 [PATCH net-next v3 0/4] selftests: openvswitch: pylint cleanup of ovs-dpctl.py Minxi Hou
2026-09-08 17:18 ` [PATCH net-next v3 1/4] selftests: openvswitch: convert %-formatting to f-strings Minxi Hou
2026-09-08 17:18 ` [PATCH net-next v3 2/4] selftests: openvswitch: fix misc pylint warnings in ovs-dpctl.py Minxi Hou
2026-09-08 17:18 ` [PATCH net-next v3 3/4] selftests: openvswitch: add missing docstrings " Minxi Hou
2026-09-10 17:22   ` netdev-bot+sashiko
2026-09-10 17:44     ` Aaron Conole
2026-09-08 17:18 ` [PATCH net-next v3 4/4] selftests: openvswitch: suppress pylint complexity warnings Minxi Hou
2026-09-10 17:22   ` netdev-bot+sashiko

This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox