From 61efc5d53e61929489a1fefe3639e0ec203aedef Mon Sep 17 00:00:00 2001
From: Samuel Gaist <samuel.gaist@idiap.ch>
Date: Thu, 10 Jan 2019 13:45:36 +0100
Subject: [PATCH] [doc] Add script to generate list of dependencies license

Requires pip-licenses
---
 doc/get_dep_licenses.py | 203 ++++++++++++++++++++++++++++++++++++++++
 1 file changed, 203 insertions(+)
 create mode 100755 doc/get_dep_licenses.py

diff --git a/doc/get_dep_licenses.py b/doc/get_dep_licenses.py
new file mode 100755
index 00000000..46a73624
--- /dev/null
+++ b/doc/get_dep_licenses.py
@@ -0,0 +1,203 @@
+#!/usr/bin/env python
+# vim: set fileencoding=utf-8 :
+
+###############################################################################
+#                                                                             #
+# Copyright (c) 2019 Idiap Research Institute, http://www.idiap.ch/           #
+# Contact: beat.support@idiap.ch                                              #
+#                                                                             #
+# This file is part of the beat.editor module of the BEAT platform.           #
+#                                                                             #
+# Commercial License Usage                                                    #
+# Licensees holding valid commercial BEAT licenses may use this file in       #
+# accordance with the terms contained in a written agreement between you      #
+# and Idiap. For further information contact tto@idiap.ch                     #
+#                                                                             #
+# Alternatively, this file may be used under the terms of the GNU Affero      #
+# Public License version 3 as published by the Free Software and appearing    #
+# in the file LICENSE.AGPL included in the packaging of this file.            #
+# The BEAT platform is distributed in the hope that it will be useful, but    #
+# WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY  #
+# or FITNESS FOR A PARTICULAR PURPOSE.                                        #
+#                                                                             #
+# You should have received a copy of the GNU Affero Public License along      #
+# with the BEAT platform. If not, see http://www.gnu.org/licenses/.           #
+#                                                                             #
+###############################################################################
+
+"""Dumps the license of all dependencies
+
+Usage:
+  %(prog)s [-v ... | --verbose ...]
+  %(prog)s (--help | -h)
+
+Options:
+  -h, --help                 Show this screen
+  -v, --verbose              Increases the output verbosity level
+"""
+
+import json
+import logging
+import os
+import re
+import sys
+
+from subprocess import PIPE  # nosec
+from subprocess import Popen  # nosec
+
+from docopt import docopt
+
+
+def _run_cmd(cmd_list):
+    """Run given command"""
+
+    try:
+        p = Popen(cmd_list, stdout=PIPE, stderr=PIPE)  # nosec
+    except OSError:
+        raise Exception("could not invoke %r\n" % cmd_list)
+    return json.loads(p.communicate()[0])
+
+
+def _call_conda(extra_args):
+    """ call conda with the list of extra arguments, and return the tuple
+     stdout, stderr
+     """
+
+    cmd_list = [os.environ.get("CONDA_EXE")]
+
+    cmd_list.extend(extra_args)
+
+    return _run_cmd(cmd_list)
+
+
+def _get_pip_info():
+    """ Get license info from pip"""
+
+    return _run_cmd(["pip-licenses", "--format-json"])
+
+
+def _clean_pkg_name(name):
+    """ Cleanup package name:
+        - lower case
+        - replace minus and underscore by only minus
+    """
+
+    return re.sub(r"[_-]", "-", name.lower())
+
+
+if __name__ == "__main__":
+
+    arguments = sys.argv[1:]
+
+    prog = os.path.basename(sys.argv[0])
+    completions = dict(prog=prog)
+
+    args = docopt(__doc__ % completions, argv=arguments, options_first=True)
+
+    # Setup the logging
+    formatter = logging.Formatter(
+        fmt="[%(asctime)s - License - %(name)s] %(levelname)s: %(message)s",
+        datefmt="%d/%b/%Y %H:%M:%S",
+    )
+
+    handler = logging.StreamHandler()
+    handler.setFormatter(formatter)
+
+    logger = logging.getLogger("beat.editor")
+    logger.addHandler(handler)
+
+    if args["--verbose"] == 1:
+        logger.setLevel(logging.INFO)
+    elif args["--verbose"] == 2:
+        logger.setLevel(logging.DEBUG)
+    elif args["--verbose"] >= 3:
+        logger.setLevel(logging.DEBUG)
+    else:
+        logger.setLevel(logging.WARNING)
+
+    list_args = ["list", "--json"]
+    logger.info("listing conda packages")
+    pkg_list = _call_conda(list_args)
+    logger.info("done")
+
+    info_args = ["info", "-l", "--json"]
+    info_args += ["{}={}".format(item["name"], item["version"]) for item in pkg_list]
+
+    logger.info("loading conda packages information")
+    pkg_info_list = _call_conda(info_args)
+    logger.info("done")
+
+    cleaned_pkg_info_list = []
+    logger.info("extracting exact package version data")
+    for pkg in pkg_list:
+        key = "{}={}".format(pkg["name"], pkg["version"])
+        build_string = pkg["build_string"]
+        pkg_info = [
+            item for item in pkg_info_list[key] if item["build"] == build_string
+        ]
+        if len(pkg_info) > 0:
+            cleaned_pkg_info_list.append(pkg_info[0])
+        else:
+            logger.warning(
+                "No information available for {}, {}".format(key, build_string)
+            )
+    logger.info("done")
+
+    logger.info("loading pip packages information")
+    pip_pkg_info_list = _get_pip_info()
+    logger.info("done")
+
+    logger.info("unifying data")
+    pkg_names = [_clean_pkg_name(pkg["name"]) for pkg in pkg_list]
+
+    cleaned_pkg_info_list += [
+        {k.casefold(): v for k, v in item.items()}
+        for item in pip_pkg_info_list
+        if _clean_pkg_name(item["Name"]) not in pkg_names
+    ]
+    logger.info("done")
+
+    cleaned_pkg_info_list = sorted(cleaned_pkg_info_list, key=lambda x: x["name"])
+
+    name_length = 0
+    version_length = 0
+    license_length = 0
+
+    for item in cleaned_pkg_info_list:
+        name_length = max(name_length, len(item["name"]))
+        version_length = max(version_length, len(item["version"]))
+        if "license" not in item:
+            logger.warning(
+                "Package has no license information: {}".format(item["name"])
+            )
+            item["license"] = "Unknown"
+        license_length = max(license_length, len(item["license"]))
+
+    filler = "|{0:-<{fill_name}}|{0:-<{fill_version}}|{0:-<{fill_license}}|\n".format(
+        "",
+        fill_name=name_length + 2,
+        fill_version=version_length + 2,
+        fill_license=license_length + 2,
+    )
+
+    text = filler
+    text += "| {0:<{fill_name}} | {1:<{fill_version}} | {2:<{fill_license}} |\n".format(
+        "Name",
+        "Version",
+        "License",
+        fill_name=name_length,
+        fill_version=version_length,
+        fill_license=license_length,
+    )
+
+    text += filler
+
+    for item in cleaned_pkg_info_list:
+        text += "| {name:<{fill_name}} | {version:<{fill_version}} | {license:<{fill_license}} |\n".format(
+            **item,
+            fill_name=name_length,
+            fill_version=version_length,
+            fill_license=license_length
+        )
+
+    sys.stdout.write(text)
-- 
GitLab