[PATCH 2/2] pw_maintainers_cli: download series mbox for maintainers lookup

David Marchand david.marchand at redhat.com
Fri Jul 24 18:21:42 CEST 2026


When looking up trees or maintainers for a series, the script was
fetching each patch individually just to extract filenames from diffs.
For a series with N patches, this resulted in N+1 API requests to
patchwork.

Download the series mbox once and parse filenames locally for the
list-trees and list-maintainers commands, reducing patchwork load
from N+1 requests to 2.

The set-pw-delegate command still needs to fetch individual patches
to check the delegate status, so it keeps the original behavior.

Before:
DEBUG:urllib3.connectionpool:https://patchwork.dpdk.org:443 "GET /api/1.2/series/38789/ HTTP/1.1" 200 3056
DEBUG:urllib3.connectionpool:https://patchwork.dpdk.org:443 "GET /api/1.2/patches/166911/ HTTP/1.1" 200 28237
DEBUG:urllib3.connectionpool:https://patchwork.dpdk.org:443 "GET /api/1.2/patches/166912/ HTTP/1.1" 200 21289
DEBUG:urllib3.connectionpool:https://patchwork.dpdk.org:443 "GET /api/1.2/patches/166913/ HTTP/1.1" 200 161492

After:
DEBUG:urllib3.connectionpool:https://patchwork.dpdk.org:443 "GET /api/1.2/series/38789/ HTTP/1.1" 200 3056
DEBUG:urllib3.connectionpool:https://patchwork.dpdk.org:443 "GET /series/38789/mbox/ HTTP/1.1" 200 191863

Signed-off-by: David Marchand <david.marchand at redhat.com>
---
 tools/pw_maintainers_cli.py | 59 ++++++++++++++++++++++++++++++++-----
 1 file changed, 52 insertions(+), 7 deletions(-)

diff --git a/tools/pw_maintainers_cli.py b/tools/pw_maintainers_cli.py
index 15ec0a7..abcf3c3 100755
--- a/tools/pw_maintainers_cli.py
+++ b/tools/pw_maintainers_cli.py
@@ -46,6 +46,7 @@ import sys
 import re
 import argparse
 import fnmatch
+import tempfile
 
 from requests.exceptions import HTTPError
 
@@ -58,6 +59,43 @@ from git_pw import patch as git_pw_patch
 LOG = logging.getLogger(__name__)
 
 
+def download_series_mbox(mbox_url):
+    """Download series mbox and return its content."""
+    with tempfile.NamedTemporaryFile(mode='w', suffix='.mbox', delete=False) as f:
+        mbox_path = f.name
+
+    LOG.debug('GET %s', mbox_url)
+    api.download(mbox_url, None, mbox_path)
+
+    with open(mbox_path, 'r') as f:
+        content = f.read()
+    os.unlink(mbox_path)
+    return content
+
+
+def find_filenames_in_mbox(mbox_content):
+    """Extract changed filenames from an mbox file.
+
+    Parses the mbox content to find all diff headers and extracts
+    the filenames being modified.
+    """
+    filenames = {}
+    filename_re = re.compile(r'^(---|\+\+\+) (\S+)')
+
+    for line in mbox_content.split('\n'):
+        filename_match = filename_re.match(line)
+        if not filename_match:
+            continue
+        filename = filename_match.group(2)
+        if filename.startswith('/dev/null'):
+            continue
+        # Strip the a/ or b/ prefix
+        filename = '/'.join(filename.split('/')[1:])
+        if filename:
+            filenames[filename] = True
+
+    return sorted(filenames.keys())
+
 MAINTAINERS_FILE_PATH = os.environ.get('MAINTAINERS_FILE_PATH')
 if not MAINTAINERS_FILE_PATH:
     print('MAINTAINERS_FILE_PATH is not set.', file=sys.stderr)
@@ -383,17 +421,24 @@ if __name__ == '__main__':
     maintainers = Maintainers()
 
     patch_list = []
+    files = []
     if resource_type == 'patch':
         patch_list.append(_git_pw.api_get('patches', _id))
+        for patch in patch_list:
+            files += Diff.find_filenames(patch['diff'])
     else:
         series = _git_pw.api_get('series', _id)
-        patch_list = [
-                _git_pw.api_get('patches', patch['id'])
-                for patch in series['patches']]
-
-    files = []
-    for patch in patch_list:
-        files += Diff.find_filenames(patch['diff'])
+        if command == 'set-pw-delegate':
+            # Need full patch data for delegate status
+            patch_list = [
+                    _git_pw.api_get('patches', patch['id'])
+                    for patch in series['patches']]
+            for patch in patch_list:
+                files += Diff.find_filenames(patch['diff'])
+        else:
+            # Download mbox once to extract filenames (2 requests instead of N+1)
+            mbox_content = download_series_mbox(series['mbox'])
+            files = find_filenames_in_mbox(mbox_content)
 
     tree = maintainers.get_tree(files)
 
-- 
2.54.0



More information about the ci mailing list