#!/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")