At present the patchwork implementation is very simple, just consisting
of a function which calls the REST API.

We want to create a fake patchwork for use in tests. So start a new
module to encapsulate communication with the patchwork server.

Use asyncio since it is easier to handle lots of concurrent requests
from different parts of the code.

Signed-off-by: Simon Glass <s...@chromium.org>
---

 tools/patman/patchwork.py   | 66 +++++++++++++++++++++++++++++++++++++
 tools/patman/pyproject.toml |  2 +-
 2 files changed, 67 insertions(+), 1 deletion(-)
 create mode 100644 tools/patman/patchwork.py

diff --git a/tools/patman/patchwork.py b/tools/patman/patchwork.py
new file mode 100644
index 00000000000..0456f98e83c
--- /dev/null
+++ b/tools/patman/patchwork.py
@@ -0,0 +1,66 @@
+# SPDX-License-Identifier: GPL-2.0+
+#
+# Copyright 2025 Simon Glass <s...@chromium.org>
+#
+"""Provides a basic API for the patchwork server
+"""
+
+import asyncio
+
+import aiohttp
+
+# Number of retries
+RETRIES = 3
+
+# Max concurrent request
+MAX_CONCURRENT = 50
+
+class Patchwork:
+    """Class to handle communication with patchwork
+    """
+    def __init__(self, url, show_progress=True):
+        """Set up a new patchwork handler
+
+        Args:
+            url (str): URL of patchwork server, e.g.
+               'https://patchwork.ozlabs.org'
+        """
+        self.url = url
+        self.proj_id = None
+        self.link_name = None
+        self._show_progress = show_progress
+        self.semaphore = asyncio.Semaphore(MAX_CONCURRENT)
+        self.request_count = 0
+
+    async def _request(self, client, subpath):
+        """Call the patchwork API and return the result as JSON
+
+        Args:
+            client (aiohttp.ClientSession): Session to use
+            subpath (str): URL subpath to use
+
+        Returns:
+            dict: Json result
+
+        Raises:
+            ValueError: the URL could not be read
+        """
+        # print('subpath', subpath)
+        self.request_count += 1
+
+        full_url = f'{self.url}/api/1.2/{subpath}'
+        async with self.semaphore:
+            # print('full_url', full_url)
+            for i in range(RETRIES + 1):
+                try:
+                    async with client.get(full_url) as response:
+                        if response.status != 200:
+                            raise ValueError(
+                                f"Could not read URL '{full_url}'")
+                        result = await response.json()
+                        # print('- done', full_url)
+                        return result
+                    break
+                except aiohttp.client_exceptions.ServerDisconnectedError:
+                    if i == RETRIES:
+                        raise
diff --git a/tools/patman/pyproject.toml b/tools/patman/pyproject.toml
index fcefcf66960..06e169cdf48 100644
--- a/tools/patman/pyproject.toml
+++ b/tools/patman/pyproject.toml
@@ -8,7 +8,7 @@ version = "0.0.6"
 authors = [
   { name="Simon Glass", email="s...@chromium.org" },
 ]
-dependencies = ["u_boot_pylib >= 0.0.6"]
+dependencies = ["u_boot_pylib >= 0.0.6", "aiohttp >= 3.9.1" ]
 description = "Patman patch manager"
 readme = "README.rst"
 requires-python = ">=3.7"
-- 
2.43.0

Reply via email to