[PATCH 17/30] patman: Move Patch and Review to patchwork module
Simon Glass
sjg at chromium.org
Tue Apr 29 15:22:14 CEST 2025
These relate to information obtained from the patchwork server, so move
their definition into the new patchwork module.
Signed-off-by: Simon Glass <sjg at chromium.org>
---
tools/patman/func_test.py | 21 +++----
tools/patman/patchwork.py | 113 ++++++++++++++++++++++++++++++++++++
tools/patman/status.py | 119 ++------------------------------------
3 files changed, 129 insertions(+), 124 deletions(-)
diff --git a/tools/patman/func_test.py b/tools/patman/func_test.py
index b3145612f9f..50fb53787d8 100644
--- a/tools/patman/func_test.py
+++ b/tools/patman/func_test.py
@@ -20,6 +20,7 @@ from patman.commit import Commit
from patman import control
from patman import patchstream
from patman.patchstream import PatchStream
+from patman import patchwork
from patman import send
from patman.series import Series
from patman import settings
@@ -807,7 +808,7 @@ diff --git a/lib/efi_loader/efi_memory.c b/lib/efi_loader/efi_memory.c
def test_parse_subject(self):
"""Test parsing of the patch subject"""
- patch = status.Patch('1')
+ patch = patchwork.Patch('1')
# Simple patch not in a series
patch.parse_subject('Testing')
@@ -876,11 +877,11 @@ diff --git a/lib/efi_loader/efi_memory.c b/lib/efi_loader/efi_memory.c
commit3 = Commit('3456')
commit3.subject = 'Subject 2'
- patch1 = status.Patch('1')
+ patch1 = patchwork.Patch('1')
patch1.subject = 'Subject 1'
- patch2 = status.Patch('2')
+ patch2 = patchwork.Patch('2')
patch2.subject = 'Subject 2'
- patch3 = status.Patch('3')
+ patch3 = patchwork.Patch('3')
patch3.subject = 'Subject 2'
series = Series()
@@ -976,7 +977,7 @@ diff --git a/lib/efi_loader/efi_memory.c b/lib/efi_loader/efi_memory.c
commit2 = Commit('ef12')
commit2.subject = 'Subject 2'
- patch1 = status.Patch('1')
+ patch1 = patchwork.Patch('1')
patch1.parse_subject('[1/2] Subject 1')
patch1.name = patch1.raw_subject
patch1.content = 'This is my patch content'
@@ -984,7 +985,7 @@ diff --git a/lib/efi_loader/efi_memory.c b/lib/efi_loader/efi_memory.c
patch1.comments = [comment1a]
- patch2 = status.Patch('2')
+ patch2 = patchwork.Patch('2')
patch2.parse_subject('[2/2] Subject 2')
patch2.name = patch2.raw_subject
patch2.content = 'Some other patch content'
@@ -1120,7 +1121,7 @@ diff --git a/lib/efi_loader/efi_memory.c b/lib/efi_loader/efi_memory.c
series = patchstream.get_metadata_for_list(branch, gitdir, count)
self.assertEqual(2, len(series.commits))
- patch1 = status.Patch('1')
+ patch1 = patchwork.Patch('1')
patch1.parse_subject('[1/2] %s' % series.commits[0].subject)
patch1.name = patch1.raw_subject
patch1.content = 'This is my patch content'
@@ -1128,7 +1129,7 @@ diff --git a/lib/efi_loader/efi_memory.c b/lib/efi_loader/efi_memory.c
patch1.comments = [comment1a]
- patch2 = status.Patch('2')
+ patch2 = patchwork.Patch('2')
patch2.parse_subject('[2/2] %s' % series.commits[1].subject)
patch2.name = patch2.raw_subject
patch2.content = 'Some other patch content'
@@ -1291,7 +1292,7 @@ line8
commit2 = Commit('ef12')
commit2.subject = 'Subject 2'
- patch1 = status.Patch('1')
+ patch1 = patchwork.Patch('1')
patch1.parse_subject('[1/2] Subject 1')
patch1.name = patch1.raw_subject
patch1.content = 'This is my patch content'
@@ -1312,7 +1313,7 @@ Reviewed-by: %s
patch1.comments = [comment1a]
- patch2 = status.Patch('2')
+ patch2 = patchwork.Patch('2')
patch2.parse_subject('[2/2] Subject 2')
patch2.name = patch2.raw_subject
patch2.content = 'Some other patch content'
diff --git a/tools/patman/patchwork.py b/tools/patman/patchwork.py
index 0456f98e83c..bb3645455fe 100644
--- a/tools/patman/patchwork.py
+++ b/tools/patman/patchwork.py
@@ -6,6 +6,7 @@
"""
import asyncio
+import re
import aiohttp
@@ -15,6 +16,118 @@ RETRIES = 3
# Max concurrent request
MAX_CONCURRENT = 50
+# Patches which are part of a multi-patch series are shown with a prefix like
+# [prefix, version, sequence], for example '[RFC, v2, 3/5]'. All but the last
+# part is optional. This decodes the string into groups. For single patches
+# the [] part is not present:
+# Groups: (ignore, ignore, ignore, prefix, version, sequence, subject)
+RE_PATCH = re.compile(r'(\[(((.*),)?(.*),)?(.*)\]\s)?(.*)$')
+
+# This decodes the sequence string into a patch number and patch count
+RE_SEQ = re.compile(r'(\d+)/(\d+)')
+
+
+class Patch(dict):
+ """Models a patch in patchwork
+
+ This class records information obtained from patchwork
+
+ Some of this information comes from the 'Patch' column:
+
+ [RFC,v2,1/3] dm: Driver and uclass changes for tiny-dm
+
+ This shows the prefix, version, seq, count and subject.
+
+ The other properties come from other columns in the display.
+
+ Properties:
+ pid (str): ID of the patch (typically an integer)
+ seq (int): Sequence number within series (1=first) parsed from sequence
+ string
+ count (int): Number of patches in series, parsed from sequence string
+ raw_subject (str): Entire subject line, e.g.
+ "[1/2,v2] efi_loader: Sort header file ordering"
+ prefix (str): Prefix string or None (e.g. 'RFC')
+ version (str): Version string or None (e.g. 'v2')
+ raw_subject (str): Raw patch subject
+ subject (str): Patch subject with [..] part removed (same as commit
+ subject)
+ """
+ def __init__(self, pid):
+ super().__init__()
+ self.id = pid # Use 'id' to match what the Rest API provides
+ self.seq = None
+ self.count = None
+ self.prefix = None
+ self.version = None
+ self.raw_subject = None
+ self.subject = None
+
+ # These make us more like a dictionary
+ def __setattr__(self, name, value):
+ self[name] = value
+
+ def __getattr__(self, name):
+ return self[name]
+
+ def __hash__(self):
+ return hash(frozenset(self.items()))
+
+ def __str__(self):
+ return self.raw_subject
+
+ def parse_subject(self, raw_subject):
+ """Parse the subject of a patch into its component parts
+
+ See RE_PATCH for details. The parsed info is placed into seq, count,
+ prefix, version, subject
+
+ Args:
+ raw_subject (str): Subject string to parse
+
+ Raises:
+ ValueError: the subject cannot be parsed
+ """
+ self.raw_subject = raw_subject.strip()
+ mat = RE_PATCH.search(raw_subject.strip())
+ if not mat:
+ raise ValueError(f"Cannot parse subject '{raw_subject}'")
+ self.prefix, self.version, seq_info, self.subject = mat.groups()[3:]
+ mat_seq = RE_SEQ.match(seq_info) if seq_info else False
+ if mat_seq is None:
+ self.version = seq_info
+ seq_info = None
+ if self.version and not self.version.startswith('v'):
+ self.prefix = self.version
+ self.version = None
+ if seq_info:
+ if mat_seq:
+ self.seq = int(mat_seq.group(1))
+ self.count = int(mat_seq.group(2))
+ else:
+ self.seq = 1
+ self.count = 1
+
+
+class Review:
+ """Represents a single review email collected in Patchwork
+
+ Patches can attract multiple reviews. Each consists of an author/date and
+ a variable number of 'snippets', which are groups of quoted and unquoted
+ text.
+ """
+ def __init__(self, meta, snippets):
+ """Create new Review object
+
+ Args:
+ meta (str): Text containing review author and date
+ snippets (list): List of snippets in th review, each a list of text
+ lines
+ """
+ self.meta = ' : '.join([line for line in meta.splitlines() if line])
+ self.snippets = snippets
+
+
class Patchwork:
"""Class to handle communication with patchwork
"""
diff --git a/tools/patman/status.py b/tools/patman/status.py
index 5fb436e08ff..8edb4ced449 100644
--- a/tools/patman/status.py
+++ b/tools/patman/status.py
@@ -11,25 +11,16 @@ collected from patchwork.
import collections
import concurrent.futures
from itertools import repeat
-import re
import pygit2
import requests
-from patman import patchstream
-from patman.patchstream import PatchStream
from u_boot_pylib import terminal
from u_boot_pylib import tout
+from patman import patchstream
+from patman import patchwork
+from patman.patchstream import PatchStream
-# Patches which are part of a multi-patch series are shown with a prefix like
-# [prefix, version, sequence], for example '[RFC, v2, 3/5]'. All but the last
-# part is optional. This decodes the string into groups. For single patches
-# the [] part is not present:
-# Groups: (ignore, ignore, ignore, prefix, version, sequence, subject)
-RE_PATCH = re.compile(r'(\[(((.*),)?(.*),)?(.*)\]\s)?(.*)$')
-
-# This decodes the sequence string into a patch number and patch count
-RE_SEQ = re.compile(r'(\d+)/(\d+)')
def to_int(vals):
"""Convert a list of strings into integers, using 0 if not an integer
@@ -44,106 +35,6 @@ def to_int(vals):
return out
-class Patch(dict):
- """Models a patch in patchwork
-
- This class records information obtained from patchwork
-
- Some of this information comes from the 'Patch' column:
-
- [RFC,v2,1/3] dm: Driver and uclass changes for tiny-dm
-
- This shows the prefix, version, seq, count and subject.
-
- The other properties come from other columns in the display.
-
- Properties:
- pid (str): ID of the patch (typically an integer)
- seq (int): Sequence number within series (1=first) parsed from sequence
- string
- count (int): Number of patches in series, parsed from sequence string
- raw_subject (str): Entire subject line, e.g.
- "[1/2,v2] efi_loader: Sort header file ordering"
- prefix (str): Prefix string or None (e.g. 'RFC')
- version (str): Version string or None (e.g. 'v2')
- raw_subject (str): Raw patch subject
- subject (str): Patch subject with [..] part removed (same as commit
- subject)
- """
- def __init__(self, pid):
- super().__init__()
- self.id = pid # Use 'id' to match what the Rest API provides
- self.seq = None
- self.count = None
- self.prefix = None
- self.version = None
- self.raw_subject = None
- self.subject = None
-
- # These make us more like a dictionary
- def __setattr__(self, name, value):
- self[name] = value
-
- def __getattr__(self, name):
- return self[name]
-
- def __hash__(self):
- return hash(frozenset(self.items()))
-
- def __str__(self):
- return self.raw_subject
-
- def parse_subject(self, raw_subject):
- """Parse the subject of a patch into its component parts
-
- See RE_PATCH for details. The parsed info is placed into seq, count,
- prefix, version, subject
-
- Args:
- raw_subject (str): Subject string to parse
-
- Raises:
- ValueError: the subject cannot be parsed
- """
- self.raw_subject = raw_subject.strip()
- mat = RE_PATCH.search(raw_subject.strip())
- if not mat:
- raise ValueError("Cannot parse subject '%s'" % raw_subject)
- self.prefix, self.version, seq_info, self.subject = mat.groups()[3:]
- mat_seq = RE_SEQ.match(seq_info) if seq_info else False
- if mat_seq is None:
- self.version = seq_info
- seq_info = None
- if self.version and not self.version.startswith('v'):
- self.prefix = self.version
- self.version = None
- if seq_info:
- if mat_seq:
- self.seq = int(mat_seq.group(1))
- self.count = int(mat_seq.group(2))
- else:
- self.seq = 1
- self.count = 1
-
-
-class Review:
- """Represents a single review email collected in Patchwork
-
- Patches can attract multiple reviews. Each consists of an author/date and
- a variable number of 'snippets', which are groups of quoted and unquoted
- text.
- """
- def __init__(self, meta, snippets):
- """Create new Review object
-
- Args:
- meta (str): Text containing review author and date
- snippets (list): List of snippets in th review, each a list of text
- lines
- """
- self.meta = ' : '.join([line for line in meta.splitlines() if line])
- self.snippets = snippets
-
def compare_with_series(series, patches):
"""Compare a list of patches with a series it came from
@@ -253,7 +144,7 @@ def collect_patches(series, series_id, url, rest_api=call_rest_api):
# Work through each row (patch) one at a time, collecting the information
warn_count = 0
for pw_patch in patch_dict:
- patch = Patch(pw_patch['id'])
+ patch = patchwork.Patch(pw_patch['id'])
patch.parse_subject(pw_patch['name'])
patches.append(patch)
if warn_count > 1:
@@ -304,7 +195,7 @@ def find_new_responses(new_rtag_list, review_list, seq, cmt, patch, url,
if pstrm.snippets:
submitter = comment['submitter']
person = '%s <%s>' % (submitter['name'], submitter['email'])
- reviews.append(Review(person, pstrm.snippets))
+ reviews.append(patchwork.Review(person, pstrm.snippets))
for response, people in pstrm.commit.rtags.items():
rtags[response].update(people)
--
2.43.0
More information about the U-Boot
mailing list