#!/usr/bin/env python # -*- coding: utf-8 -*- import asyncio import shutil from pathlib import Path import typer from metagpt.actions.rebuild_class_view import RebuildClassView from metagpt.actions.rebuild_sequence_view import RebuildSequenceView from metagpt.context import Context from metagpt.llm import LLM from metagpt.logs import logger from metagpt.utils.git_repository import GitRepository from metagpt.utils.project_repo import ProjectRepo app = typer.Typer(add_completion=False, pretty_exceptions_show_locals=False) @app.command("", help="Python project reverse engineering.") def startup( project_root: str = typer.Argument( default="", help="Specify the root directory of the existing project for reverse engineering.", ), output_dir: str = typer.Option(default="", help="Specify the output directory path for reverse engineering."), ): package_root = Path(project_root) if not package_root.exists(): raise FileNotFoundError(f"{project_root} not exists") if not _is_python_package_root(package_root): raise FileNotFoundError(f'There are no "*.py" files under "{project_root}".') init_file = package_root / "__init__.py" # used by pyreverse init_file_exists = init_file.exists() if not init_file_exists: init_file.touch() if not output_dir: output_dir = package_root / "../reverse_engineering_output" logger.info(f"output dir:{output_dir}") try: asyncio.run(reverse_engineering(package_root, Path(output_dir))) finally: if not init_file_exists: init_file.unlink(missing_ok=True) tmp_dir = package_root / "__dot__" if tmp_dir.exists(): shutil.rmtree(tmp_dir, ignore_errors=True) def _is_python_package_root(package_root: Path) -> bool: for file_path in package_root.iterdir(): if file_path.is_file(): if file_path.suffix == ".py": return True return False async def reverse_engineering(package_root: Path, output_dir: Path): ctx = Context() ctx.git_repo = GitRepository(output_dir) ctx.repo = ProjectRepo(ctx.git_repo) action = RebuildClassView(name="ReverseEngineering", i_context=str(package_root), llm=LLM(), context=ctx) await action.run() action = RebuildSequenceView(name="ReverseEngineering", llm=LLM(), context=ctx) await action.run() if __name__ == "__main__": app()