get:
Show a patch.

patch:
Update a patch.

put:
Update a patch.

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

{
    "id": 96736,
    "url": "http://patches.dpdk.org/api/patches/96736/?format=api",
    "web_url": "http://patches.dpdk.org/project/dpdk/patch/20210809125351.914158-3-mdr@ashroe.eu/",
    "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": "<20210809125351.914158-3-mdr@ashroe.eu>",
    "list_archive_url": "https://inbox.dpdk.org/dev/20210809125351.914158-3-mdr@ashroe.eu",
    "date": "2021-08-09T12:53:51",
    "name": "[v9,2/2] devtools: script to send notifications of expired symbols",
    "commit_ref": null,
    "pull_url": null,
    "state": "superseded",
    "archived": true,
    "hash": "e3a940b5ac0279e5972ad4e4c94451506ab70f2a",
    "submitter": {
        "id": 1310,
        "url": "http://patches.dpdk.org/api/people/1310/?format=api",
        "name": "Ray Kinsella",
        "email": "mdr@ashroe.eu"
    },
    "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/20210809125351.914158-3-mdr@ashroe.eu/mbox/",
    "series": [
        {
            "id": 18239,
            "url": "http://patches.dpdk.org/api/series/18239/?format=api",
            "web_url": "http://patches.dpdk.org/project/dpdk/list/?series=18239",
            "date": "2021-08-09T12:53:49",
            "name": "devtools: scripts to count and track symbols",
            "version": 9,
            "mbox": "http://patches.dpdk.org/series/18239/mbox/"
        }
    ],
    "comments": "http://patches.dpdk.org/api/patches/96736/comments/",
    "check": "warning",
    "checks": "http://patches.dpdk.org/api/patches/96736/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 C15A5A0C4C;\n\tMon,  9 Aug 2021 14:56:16 +0200 (CEST)",
            "from [217.70.189.124] (localhost [127.0.0.1])\n\tby mails.dpdk.org (Postfix) with ESMTP id 4CB3941109;\n\tMon,  9 Aug 2021 14:56:07 +0200 (CEST)",
            "from mga11.intel.com (mga11.intel.com [192.55.52.93])\n by mails.dpdk.org (Postfix) with ESMTP id F130C41109\n for <dev@dpdk.org>; Mon,  9 Aug 2021 14:56:04 +0200 (CEST)",
            "from fmsmga003.fm.intel.com ([10.253.24.29])\n by fmsmga102.fm.intel.com with ESMTP/TLS/ECDHE-RSA-AES256-GCM-SHA384;\n 09 Aug 2021 05:56:04 -0700",
            "from silpixa00396680.ir.intel.com (HELO\n silpixa00396680.ger.corp.intel.com) ([10.237.223.54])\n by FMSMGA003.fm.intel.com with ESMTP; 09 Aug 2021 05:56:02 -0700"
        ],
        "X-IronPort-AV": [
            "E=McAfee;i=\"6200,9189,10070\"; a=\"211578155\"",
            "E=Sophos;i=\"5.84,307,1620716400\"; d=\"scan'208\";a=\"211578155\"",
            "E=Sophos;i=\"5.84,307,1620716400\"; d=\"scan'208\";a=\"514940444\""
        ],
        "X-ExtLoop1": "1",
        "From": "Ray Kinsella <mdr@ashroe.eu>",
        "To": "dev@dpdk.org",
        "Cc": "bruce.richardson@intel.com, stephen@networkplumber.org,\n ferruh.yigit@intel.com, thomas@monjalon.net, ktraynor@redhat.com,\n mdr@ashroe.eu",
        "Date": "Mon,  9 Aug 2021 13:53:51 +0100",
        "Message-Id": "<20210809125351.914158-3-mdr@ashroe.eu>",
        "X-Mailer": "git-send-email 2.26.2",
        "In-Reply-To": "<20210809125351.914158-1-mdr@ashroe.eu>",
        "References": "<20210618163659.85933-1-mdr@ashroe.eu>\n <20210809125351.914158-1-mdr@ashroe.eu>",
        "MIME-Version": "1.0",
        "Content-Transfer-Encoding": "8bit",
        "Subject": "[dpdk-dev] [PATCH v9 2/2] devtools: script to send notifications of\n expired symbols",
        "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",
        "Sender": "\"dev\" <dev-bounces@dpdk.org>"
    },
    "content": "Use this script with the output of the DPDK symbol tool, to notify\nmaintainers of expired symbols by email. You need to define the environment\nvariable DPDK_GETMAINTAINER_PATH for this tool to work.\n\nUse terminal output to review the emails before sending.\ne.g.\n$ devtools/symbol-tool.py list-expired --format-output csv \\\n| DPDK_GETMAINTAINER_PATH=<somewhere>/get_maintainer.pl \\\ndevtools/notify_expired_symbols.py --format-output terminal\n\nThen use email output to send the emails to the maintainers.\ne.g.\n$ devtools/symbol-tool.py list-expired --format-output csv \\\n| DPDK_GETMAINTAINER_PATH=<somewhere>/get_maintainer.pl \\\ndevtools/notify_expired_symbols.py --format-output email \\\n--smtp-server <server> --sender <someone@somewhere.com> \\\n--password <password>\n\nSigned-off-by: Ray Kinsella <mdr@ashroe.eu>\n---\n devtools/notify-symbol-maintainers.py | 234 ++++++++++++++++++++++++++\n 1 file changed, 234 insertions(+)\n create mode 100755 devtools/notify-symbol-maintainers.py",
    "diff": "diff --git a/devtools/notify-symbol-maintainers.py b/devtools/notify-symbol-maintainers.py\nnew file mode 100755\nindex 0000000000..a6c27b067c\n--- /dev/null\n+++ b/devtools/notify-symbol-maintainers.py\n@@ -0,0 +1,234 @@\n+#!/usr/bin/env python3\n+# SPDX-License-Identifier: BSD-3-Clause\n+# Copyright(c) 2021 Intel Corporation\n+'''Tool to notify maintainers of expired symbols'''\n+import smtplib\n+import ssl\n+import sys\n+import subprocess\n+import argparse\n+from argparse import RawTextHelpFormatter\n+import time\n+from email.message import EmailMessage\n+\n+DESCRIPTION = '''\n+Use this script with the output of the DPDK symbol tool, to notify maintainers\n+of expired symbols by email. You need to define the environment variable\n+DPDK_GETMAINTAINER_PATH, for this tool to work.\n+\n+Use terminal output to review the emails before sending.\n+e.g.\n+$ devtools/symbol-tool.py list-expired --format-output csv \\\\\n+| DPDK_GETMAINTAINER_PATH=<somewhere>/get_maintainer.pl \\\\\n+{s} --format-output terminal\n+\n+Then use email output to send the emails to the maintainers.\n+e.g.\n+$ devtools/symbol-tool.py list-expired --format-output csv \\\\\n+| DPDK_GETMAINTAINER_PATH=<somewhere>/get_maintainer.pl \\\\\n+{s} --format-output email \\\\\n+--smtp-server <server> --sender <someone@somewhere.com> --password <password>\n+'''\n+\n+EMAIL_TEMPLATE = '''Hi there,\n+\n+Please note the symbols listed below have expired. In line with the DPDK ABI\n+policy, they should be scheduled for removal, in the next DPDK release.\n+\n+For more information, please see the DPDK ABI Policy, section 3.5.3.\n+https://doc.dpdk.org/guides/contributing/abi_policy.html\n+\n+Thanks,\n+\n+The DPDK Symbol Bot\n+\n+'''\n+\n+ABI_POLICY = 'doc/guides/contributing/abi_policy.rst'\n+get_maintainer = ['devtools/get-maintainer.sh', \\\n+                  '--email', '-f']\n+\n+def _get_maintainers(libpath):\n+    '''Get the maintainers for given library'''\n+    try:\n+        cmd = get_maintainer + [libpath]\n+        result = subprocess.run(cmd, \\\n+                                stdout=subprocess.PIPE, \\\n+                                stderr=subprocess.PIPE,\n+                                check=True)\n+    except subprocess.CalledProcessError:\n+        return None\n+\n+    if result is None:\n+        return None\n+\n+    email = result.stdout.decode('utf-8')\n+    if email == '':\n+        return None\n+\n+    email = list(filter(None,email.split('\\n')))\n+    return email\n+\n+default_maintainers = _get_maintainers(ABI_POLICY)\n+\n+def get_maintainers(libpath):\n+    '''Get the maintainers for given library'''\n+    maintainers=_get_maintainers(libpath)\n+\n+    if maintainers is None:\n+        maintainers = default_maintainers\n+\n+    return maintainers\n+\n+def get_message(library, symbols):\n+    '''Build email message from symbols, config and maintainers'''\n+    message = {}\n+    maintainers = get_maintainers(library)\n+\n+    message['To'] = maintainers\n+    if maintainers != default_maintainers:\n+        message['CC'] = default_maintainers\n+\n+    message['Subject'] = 'Expired symbols in {}\\n'.format(library)\n+\n+    body = EMAIL_TEMPLATE\n+    for sym in symbols:\n+        body += ('{}\\n'.format(sym))\n+\n+    message['Body'] = body\n+\n+    return message\n+\n+class OutputEmail():\n+    '''Format the output for email'''\n+    def __init__(self, config):\n+        self.config = config\n+\n+        self.terminal = OutputTerminal(config)\n+        context = ssl.create_default_context()\n+\n+        # Try to log in to server and send email\n+        try:\n+            self.server = smtplib.SMTP(config['smtp_server'], 587)\n+            self.server.starttls(context=context) # Secure the connection\n+            self.server.login(config['sender'], config['password'])\n+        except Exception as exception:\n+            print(exception)\n+            raise exception\n+\n+    def message(self,message):\n+        '''send email'''\n+        self.terminal.message(message)\n+\n+        msg = EmailMessage()\n+        msg.set_content(message.pop('Body'))\n+\n+        for key in message.keys():\n+            msg[key] = message[key]\n+\n+        msg['From'] = self.config['sender']\n+        msg['Reply-To'] = 'no-reply@dpdk.org'\n+\n+        self.server.send_message(msg)\n+\n+        time.sleep(1)\n+\n+    def __del__(self):\n+        self.server.quit()\n+\n+class OutputTerminal(): # pylint: disable=too-few-public-methods\n+    '''Format the output for the terminal'''\n+    def __init__(self, config):\n+        self.config = config\n+\n+    def message(self,message):\n+        '''Print email to terminal'''\n+        terminal = 'To:' + ', '.join(message['To']) + '\\n'\n+        if 'sender' in self.config.keys():\n+            terminal += 'From:' + self.config['sender'] + '\\n'\n+\n+        terminal += 'Reply-To:' + 'no-reply@dpdk.org' + '\\n'\n+        if 'CC' in message.keys():\n+            terminal += 'CC:' + ', '.join(message['CC']) + '\\n'\n+\n+        terminal += 'Subject:' + message['Subject'] + '\\n'\n+        terminal += 'Body:' + message['Body'] + '\\n'\n+\n+        print(terminal)\n+        print('-' * 80)\n+\n+def parse_config(args):\n+    '''put the command line args in the right places'''\n+    config = {}\n+    error_msg = None\n+\n+    outputs = {\n+        None : OutputTerminal,\n+        'terminal' : OutputTerminal,\n+        'email' : OutputEmail\n+    }\n+\n+    if args.format_output == 'email':\n+        if args.smtp_server is None:\n+            error_msg = 'SMTP server'\n+        else:\n+            config['smtp_server'] = args.smtp_server\n+\n+        if args.sender is None:\n+            error_msg = 'sender'\n+        else:\n+            config['sender'] = args.sender\n+\n+        if args.password is None:\n+            error_msg = 'password'\n+        else:\n+            config['password'] = args.password\n+\n+    if error_msg is not None:\n+        print('Please specify a {} for email output'.format(error_msg))\n+        return None\n+\n+    config['output'] = outputs[args.format_output]\n+    return config\n+\n+def main():\n+    '''Main entry point'''\n+    parser = argparse.ArgumentParser(description=DESCRIPTION.format(s=__file__), \\\n+                                     formatter_class=RawTextHelpFormatter)\n+    parser.add_argument('--format-output', choices=['terminal','email'], \\\n+                        default='terminal')\n+    parser.add_argument('--smtp-server')\n+    parser.add_argument('--password')\n+    parser.add_argument('--sender')\n+\n+    args = parser.parse_args()\n+    config = parse_config(args)\n+    if config is None:\n+        return\n+\n+    symbols = []\n+    lastlib = library = ''\n+\n+    output = config['output'](config)\n+\n+    for line in sys.stdin:\n+        line = line.rstrip('\\n')\n+        library, symbol = [line[:line.find(',')], \\\n+                           line[line.find(',') + 1: len(line)]]\n+        if library == 'mapfile':\n+            continue\n+\n+        if library != lastlib:\n+            message = get_message(lastlib, symbols)\n+            output.message(message)\n+            symbols = []\n+\n+        lastlib = library\n+        symbols = symbols + [symbol]\n+\n+    #print the last library\n+    message = get_message(lastlib, symbols)\n+    output.message(message)\n+\n+if __name__ == '__main__':\n+    main()\n",
    "prefixes": [
        "v9",
        "2/2"
    ]
}