get:
Show a patch.

patch:
Update a patch.

put:
Update a patch.

GET /api/patches/139239/?format=api
HTTP 200 OK
Allow: GET, PUT, PATCH, HEAD, OPTIONS
Content-Type: application/json
Vary: Accept

{
    "id": 139239,
    "url": "http://patches.dpdk.org/api/patches/139239/?format=api",
    "web_url": "http://patches.dpdk.org/project/dpdk/patch/20240412111136.3470304-4-luca.vizzarro@arm.com/",
    "project": {
        "id": 1,
        "url": "http://patches.dpdk.org/api/projects/1/?format=api",
        "name": "DPDK",
        "link_name": "dpdk",
        "list_id": "dev.dpdk.org",
        "list_email": "dev@dpdk.org",
        "web_url": "http://core.dpdk.org",
        "scm_url": "git://dpdk.org/dpdk",
        "webscm_url": "http://git.dpdk.org/dpdk",
        "list_archive_url": "https://inbox.dpdk.org/dev",
        "list_archive_url_format": "https://inbox.dpdk.org/dev/{}",
        "commit_url_format": ""
    },
    "msgid": "<20240412111136.3470304-4-luca.vizzarro@arm.com>",
    "list_archive_url": "https://inbox.dpdk.org/dev/20240412111136.3470304-4-luca.vizzarro@arm.com",
    "date": "2024-04-12T11:11:34",
    "name": "[3/5] dts: add parsing utility module",
    "commit_ref": null,
    "pull_url": null,
    "state": "new",
    "archived": false,
    "hash": "9b9a0a4d86d81ccb85131674122133356c4033ff",
    "submitter": {
        "id": 3197,
        "url": "http://patches.dpdk.org/api/people/3197/?format=api",
        "name": "Luca Vizzarro",
        "email": "luca.vizzarro@arm.com"
    },
    "delegate": {
        "id": 1,
        "url": "http://patches.dpdk.org/api/users/1/?format=api",
        "username": "tmonjalo",
        "first_name": "Thomas",
        "last_name": "Monjalon",
        "email": "thomas@monjalon.net"
    },
    "mbox": "http://patches.dpdk.org/project/dpdk/patch/20240412111136.3470304-4-luca.vizzarro@arm.com/mbox/",
    "series": [
        {
            "id": 31729,
            "url": "http://patches.dpdk.org/api/series/31729/?format=api",
            "web_url": "http://patches.dpdk.org/project/dpdk/list/?series=31729",
            "date": "2024-04-12T11:11:31",
            "name": "dts: testpmd show port info/stats",
            "version": 1,
            "mbox": "http://patches.dpdk.org/series/31729/mbox/"
        }
    ],
    "comments": "http://patches.dpdk.org/api/patches/139239/comments/",
    "check": "success",
    "checks": "http://patches.dpdk.org/api/patches/139239/checks/",
    "tags": {},
    "related": [],
    "headers": {
        "Return-Path": "<dev-bounces@dpdk.org>",
        "X-Original-To": "patchwork@inbox.dpdk.org",
        "Delivered-To": "patchwork@inbox.dpdk.org",
        "Received": [
            "from mails.dpdk.org (mails.dpdk.org [217.70.189.124])\n\tby inbox.dpdk.org (Postfix) with ESMTP id 0468F43E51;\n\tFri, 12 Apr 2024 13:12:10 +0200 (CEST)",
            "from mails.dpdk.org (localhost [127.0.0.1])\n\tby mails.dpdk.org (Postfix) with ESMTP id EEE8B40A72;\n\tFri, 12 Apr 2024 13:11:57 +0200 (CEST)",
            "from foss.arm.com (foss.arm.com [217.140.110.172])\n by mails.dpdk.org (Postfix) with ESMTP id 6623A40A4B\n for <dev@dpdk.org>; Fri, 12 Apr 2024 13:11:56 +0200 (CEST)",
            "from usa-sjc-imap-foss1.foss.arm.com (unknown [10.121.207.14])\n by usa-sjc-mx-foss1.foss.arm.com (Postfix) with ESMTP id 34C5B339;\n Fri, 12 Apr 2024 04:12:25 -0700 (PDT)",
            "from localhost.localdomain (unknown [10.57.19.212])\n by usa-sjc-imap-foss1.foss.arm.com (Postfix) with ESMTPA id CEC4E3F766;\n Fri, 12 Apr 2024 04:11:54 -0700 (PDT)"
        ],
        "From": "Luca Vizzarro <luca.vizzarro@arm.com>",
        "To": "dev@dpdk.org",
        "Cc": "=?utf-8?q?Juraj_Linke=C5=A1?= <juraj.linkes@pantheon.tech>,\n Jeremy Spewock <jspewock@iol.unh.edu>, Luca Vizzarro <luca.vizzarro@arm.com>,\n Paul Szczepanek <paul.szczepanek@arm.com>",
        "Subject": "[PATCH 3/5] dts: add parsing utility module",
        "Date": "Fri, 12 Apr 2024 12:11:34 +0100",
        "Message-Id": "<20240412111136.3470304-4-luca.vizzarro@arm.com>",
        "X-Mailer": "git-send-email 2.34.1",
        "In-Reply-To": "<20240412111136.3470304-1-luca.vizzarro@arm.com>",
        "References": "<20240412111136.3470304-1-luca.vizzarro@arm.com>",
        "MIME-Version": "1.0",
        "Content-Transfer-Encoding": "8bit",
        "X-BeenThere": "dev@dpdk.org",
        "X-Mailman-Version": "2.1.29",
        "Precedence": "list",
        "List-Id": "DPDK patches and discussions <dev.dpdk.org>",
        "List-Unsubscribe": "<https://mails.dpdk.org/options/dev>,\n <mailto:dev-request@dpdk.org?subject=unsubscribe>",
        "List-Archive": "<http://mails.dpdk.org/archives/dev/>",
        "List-Post": "<mailto:dev@dpdk.org>",
        "List-Help": "<mailto:dev-request@dpdk.org?subject=help>",
        "List-Subscribe": "<https://mails.dpdk.org/listinfo/dev>,\n <mailto:dev-request@dpdk.org?subject=subscribe>",
        "Errors-To": "dev-bounces@dpdk.org"
    },
    "content": "Adds parsing text into a custom data structure. It provides a new\n`TextParser` dataclass to be inherited. This implements the `parse`\nmethod, which combined with the parser functions, it can automatically\nparse the value for each field.\n\nSigned-off-by: Luca Vizzarro <luca.vizzarro@arm.com>\nReviewed-by: Paul Szczepanek <paul.szczepanek@arm.com>\n---\n dts/framework/parser.py | 147 ++++++++++++++++++++++++++++++++++++++++\n 1 file changed, 147 insertions(+)\n create mode 100644 dts/framework/parser.py",
    "diff": "diff --git a/dts/framework/parser.py b/dts/framework/parser.py\nnew file mode 100644\nindex 0000000000..5a2ba0c93a\n--- /dev/null\n+++ b/dts/framework/parser.py\n@@ -0,0 +1,147 @@\n+# SPDX-License-Identifier: BSD-3-Clause\n+# Copyright(c) 2024 Arm Limited\n+\n+\"\"\"Parsing utility module.\n+\n+This module provides :class:`~TextParser` which can be used to model any data structure\n+that can parse a block of text.\n+\"\"\"\n+\n+from dataclasses import dataclass, fields, MISSING\n+import re\n+from typing import TypeVar\n+from typing_extensions import Self\n+\n+T = TypeVar(\"T\")\n+\n+\n+META_PARSERS = \"parsers\"\n+\n+\n+def chain(parser, metadata):\n+    \"\"\"Chain a parser function.\n+\n+    The parser function can take and return a single argument of any type. It is\n+    up to the user to ensure that the chained functions have compatible types.\n+\n+    Args:\n+        parser: the parser function pointer\n+        metadata: pre-existing metadata to chain if any\n+    \"\"\"\n+    parsers = metadata.get(META_PARSERS) or []\n+    parsers.append(parser)\n+    return {**metadata, META_PARSERS: parsers}\n+\n+\n+def to_int(metadata={}, base=0):\n+    \"\"\"Converts a string to an integer.\n+\n+    Args:\n+        metadata: pre-existing metadata to chain if any\n+        base: argument passed to the constructor of ``int``\n+    \"\"\"\n+    return chain(lambda v: int(v, base), metadata)\n+\n+\n+def eq(v2, metadata={}):\n+    \"\"\"Compares two values and returns a boolean.\n+\n+    Args:\n+        v2: value to compare with the incoming value\n+        metadata: pre-existing metadata to chain if any\n+    \"\"\"\n+    return chain(lambda v1: v1 == v2, metadata)\n+\n+\n+def to_bool(metadata={}):\n+    \"\"\"Evaluates a string into a boolean.\n+\n+    The following case-insensitive words yield ``True``: on, yes, enabled, true.\n+\n+    Args:\n+        metadata: pre-existing metadata to chain if any\n+    \"\"\"\n+    return chain(lambda s: s.lower() in [\"on\", \"yes\", \"enabled\", \"true\"], metadata)\n+\n+\n+def regex(\n+    pattern: str | re.Pattern[str],\n+    flags: re.RegexFlag = re.RegexFlag(0),\n+    named: bool = False,\n+    metadata={},\n+):\n+    \"\"\"Searches for a regular expression in a text.\n+\n+    If there is only one capture group, its value is returned, otherwise a tuple containing all the\n+    capture groups values is returned instead.\n+\n+    Args:\n+        pattern: the regular expression pattern\n+        flags: the regular expression flags\n+        named: if set to True only the named capture groups will be returned as a dictionary\n+        metadata: pre-existing metadata to chain if any\n+    \"\"\"\n+    pattern = re.compile(pattern, flags)\n+\n+    def regex_parser(text: str):\n+        m = pattern.search(text)\n+        if m is None:\n+            return m\n+\n+        if named:\n+            return m.groupdict()\n+\n+        matches = m.groups()\n+        if len(matches) == 1:\n+            return matches[0]\n+\n+        return matches\n+\n+    return chain(regex_parser, metadata)\n+\n+\n+@dataclass\n+class TextParser:\n+    \"\"\"Helper abstract dataclass that parses a text according to the fields' rules.\n+\n+    This class is accompanied by a selection of parser functions and a generic chaining function,\n+    that are to be set to the fields' metadata, to enable parsing. If a field metadata is not set with\n+    any parser function, this is skipped.\n+    \"\"\"\n+\n+    @classmethod\n+    def parse(cls, text: str) -> Self:\n+        \"\"\"The parsing class method.\n+\n+        This function loops through every field that has any parser function associated with it and runs\n+        each parser chain to the supplied text. If a parser function returns None, it expects that parsing\n+        has failed and continues to the next field.\n+\n+        Args:\n+            text: the text to parse\n+        Raises:\n+            RuntimeError: if the parser did not find a match and the field does not have a default value\n+                          or default factory.\n+        \"\"\"\n+        fields_values = {}\n+        for field in fields(cls):\n+            parsers = field.metadata.get(META_PARSERS)\n+            if parsers is None:\n+                continue\n+\n+            field_value = text\n+            for parser_fn in parsers:\n+                field_value = parser_fn(field_value)\n+                if field_value is None:\n+                    # nothing was actually parsed, move on\n+                    break\n+\n+            if field_value is None:\n+                if field.default is MISSING and field.default_factory is MISSING:\n+                    raise RuntimeError(\n+                        f\"parsers for field {field.name} returned None, but the field has no default\"\n+                    )\n+            else:\n+                fields_values[field.name] = field_value\n+\n+        return cls(**fields_values)\n",
    "prefixes": [
        "3/5"
    ]
}