#!/usr/bin/env python3

# Extract the dependencies from the setup.py files
# and save the result to requirements.txt.
#
# You can filter any extra require by adding the name as argument.
#
# Examples:
#   tools/extract_requirements.py
#   tools/extract_requirements.py dev


import ast
from glob import glob
from pathlib import Path
from sys import argv


class RemoveJoinedStr(ast.NodeTransformer):
    def visit_JoinedStr(self, _node):  # pylint: disable=invalid-name
        pass


for setup in glob("*/setup.py"):
    setup_path = Path(setup)
    requirements_path = setup_path.parent / "requirements.txt"

    lines = [
        "# Please do not edit this file, edit the setup.py file!",
        "# This file is auto-generated by tools/extract_requirements.py.",
    ]

    requires = []

    for node in ast.walk(ast.parse(setup_path.read_text(encoding="utf-8"))):
        if (
            isinstance(node, ast.Expr)
            and isinstance(node.value, ast.Call)
            and isinstance(node.value.func, ast.Name)
            and node.value.func.id == "setup"
        ):

            for keyword in node.value.keywords:
                if keyword.arg == "install_requires":
                    requires.extend(ast.literal_eval(keyword.value))

                if keyword.arg == "extras_require":
                    extras = ast.literal_eval(RemoveJoinedStr().visit(keyword.value))

                    for key, values in extras.items():
                        if key in argv:
                            continue
                        requires.extend(values)

    lines.extend(sorted(requires))

    requirements_path.write_text("\n".join(lines) + "\n", encoding="utf-8")
