Created
May 31, 2025 11:22
-
-
Save goabonga/45a1b5b69b523ef0d98e9f86e920717e to your computer and use it in GitHub Desktop.
Automated Python Project Generator with Self-Healing Tests
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| import subprocess | |
| import time | |
| import re | |
| import shutil | |
| from pathlib import Path | |
| import openai | |
| from slugify import slugify | |
| api_key = "sk-proj-..." # Replace with your OpenAI API key | |
| client = openai.OpenAI(api_key=api_key) | |
| MODEL = "gpt-4-turbo" | |
| def ask_task() -> str: | |
| return input("π¬ What do you want to build? ").strip() | |
| def run(cmd: list[str], cwd: Path | None = None) -> None: | |
| subprocess.run(cmd, cwd=cwd, check=True) | |
| def run_capture(cmd: list[str], cwd: Path | None = None) -> tuple[bool, str]: | |
| process = subprocess.run(cmd, cwd=cwd, capture_output=True, text=True) | |
| return process.returncode == 0, process.stdout + process.stderr | |
| def extract_code(text: str) -> str: | |
| match = re.search(r"```(?:python)?(.*?)```", text, re.DOTALL) | |
| return match.group(1).strip() if match else text.strip() | |
| def call_openai(messages: list[dict[str, str]]) -> str: | |
| response = client.chat.completions.create( | |
| model=MODEL, | |
| messages=messages, | |
| temperature=0, | |
| ) | |
| return extract_code(response.choices[0].message.content) | |
| def infer_project_metadata(task: str) -> tuple[str, str]: | |
| messages = [ | |
| {"role": "system", "content": "You are a Python project assistant."}, | |
| {"role": "user", "content": f"For the task: '{task}', suggest:\n" | |
| "- a readable project name (dashes allowed)\n" | |
| "- a valid Python module name (snake_case)\n" | |
| "Return in the format: project-name module_name"} | |
| ] | |
| result = call_openai(messages) | |
| lines = result.strip().splitlines() | |
| values = [] | |
| for line in lines: | |
| if ':' in line: | |
| values.append(line.split(":", 1)[1].strip()) | |
| elif line.strip(): | |
| values.extend(line.strip().split()) | |
| if len(values) >= 2: | |
| project = slugify(values[0], separator='-') | |
| module = slugify(values[1], separator='_') | |
| return project, module | |
| raise ValueError(f"Unexpected response from OpenAI: {result}") | |
| def infer_dependencies(task: str) -> list[str]: | |
| messages = [ | |
| {"role": "system", "content": "You are a Python build assistant."}, | |
| {"role": "user", "content": f"What Python packages (PyPI) are required to implement this task: '{task}'? Return only the list separated by spaces."} | |
| ] | |
| deps = call_openai(messages) | |
| return deps.split() | |
| def generate_files(task: str, project_dir: Path, module_name: str) -> None: | |
| impl_filename = module_name # enforce .py filename == module name | |
| src_path = project_dir / "src" / module_name | |
| tests_path = project_dir / "tests" | |
| src_path.mkdir(parents=True, exist_ok=True) | |
| tests_path.mkdir(parents=True, exist_ok=True) | |
| # empty __init__.py | |
| (src_path / "__init__.py").write_text("") | |
| # generate test file | |
| test_path = tests_path / f"test_{impl_filename}.py" | |
| messages_test = [ | |
| {"role": "system", "content": "You write a test file using pytest."}, | |
| {"role": "user", "content": f"Write a pytest test file for the implementation in src/{module_name}/{impl_filename}.py.\n" | |
| f"Only write tests. Do not include the implementation itself."} | |
| ] | |
| test_code = call_openai(messages_test) | |
| test_path.write_text(test_code) | |
| # generate implementation | |
| impl_path = src_path / f"{impl_filename}.py" | |
| messages_code = [ | |
| {"role": "system", "content": "You write a Python implementation module."}, | |
| {"role": "user", "content": f"Write the implementation for the following task:\n\n{task}\n\n" | |
| f"The file is: src/{module_name}/{impl_filename}.py\n" | |
| f"Only return the implementation. Do not include tests."} | |
| ] | |
| impl_code = call_openai(messages_code) | |
| impl_path.write_text(impl_code) | |
| def fix_code(current_code: str, feedback: str) -> str: | |
| messages = [ | |
| {"role": "system", "content": "You fix the code to make the tests pass."}, | |
| {"role": "user", "content": f"Here is the current code:\n\n```python\n{current_code}\n```"}, | |
| {"role": "user", "content": f"The following pytest output shows errors:\n\n{feedback}"} | |
| ] | |
| return call_openai(messages) | |
| def main() -> None: | |
| task = ask_task() | |
| project_name, module_name = infer_project_metadata(task) | |
| project_dir = Path(project_name) | |
| if project_dir.exists(): | |
| shutil.rmtree(project_dir) | |
| print(f"π Creating project: {project_name}") | |
| run(["uv", "init", "--lib", project_name]) | |
| print("π¦ Detecting dependencies...") | |
| deps = ["pytest"] + infer_dependencies(task) | |
| print(f"π¦ Dependencies: {deps}") | |
| run(["uv", "add"] + deps, cwd=project_dir) | |
| print("π Generating code and tests...") | |
| generate_files(task, project_dir, module_name) | |
| code_path = project_dir / "src" / module_name / f"{module_name}.py" | |
| iteration = 0 | |
| while True: | |
| print(f"\nπ Iteration {iteration}") | |
| success, output = run_capture(["uv", "run", "python", "-m", "pytest", "-q", "--tb=short"], cwd=project_dir) | |
| if success: | |
| print("β All tests passed.") | |
| break | |
| print("β Tests failed. Attempting fix...") | |
| current_code = code_path.read_text() | |
| new_code = fix_code(current_code, output) | |
| code_path.write_text(new_code) | |
| time.sleep(1) | |
| iteration += 1 | |
| if __name__ == "__main__": | |
| main() |
Author
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
$ tree simple-linear-regression/ simple-linear-regression/ βββ pyproject.toml βββ README.md βββ src β βββ simple_linear_regression β βββ __init__.py β βββ py.typed β βββ simple_linear_regression.py βββ tests βββ test_simple_linear_regression.pytest_simple_linear_regression.py :
simple_linear_regression.py :