PYTHONPython

scaffold

real world projects / cli tool / devtools / commands

PYTHON
scaffold.py🐍
"""
Project scaffolding commands.
"""

from pathlib import Path
import click
from rich.console import Console

console = Console()

# Project templates
TEMPLATES = {
    'python': {
        'description': 'Basic Python project',
        'files': {
            'README.md': '# {name}\n\nA Python project.\n',
            'pyproject.toml': '''[project]
name = "{name}"
version = "0.1.0"
description = ""
readme = "README.md"
requires-python = ">=3.10"
dependencies = []

[project.optional-dependencies]
dev = ["pytest", "black", "mypy"]
''',
            'src/{name}/__init__.py': '"""Package {name}."""\n',
            'src/{name}/main.py': '''"""Main module."""


def main():
    """Entry point."""
    print("Hello from {name}!")


if __name__ == "__main__":
    main()
''',
            'tests/__init__.py': '',
            'tests/test_main.py': '''"""Tests for main module."""

from {name}.main import main


def test_main():
    """Test main function runs."""
    main()  # Should not raise
''',
            '.gitignore': '''__pycache__/
*.py[cod]
.venv/
dist/
*.egg-info/
.mypy_cache/
.pytest_cache/
''',
        }
    },
    'fastapi': {
        'description': 'FastAPI project',
        'files': {
            'README.md': '# {name}\n\nA FastAPI project.\n\n## Run\n\n```bash\nuvicorn app.main:app --reload\n```\n',
            'requirements.txt': '''fastapi>=0.109.0
uvicorn[standard]>=0.27.0
pydantic>=2.0.0
''',
            'app/__init__.py': '',
            'app/main.py': '''"""FastAPI application."""

from fastapi import FastAPI

app = FastAPI(title="{name}")


@app.get("/")
async def root():
    return {{"message": "Welcome to {name}"}}


@app.get("/health")
async def health():
    return {{"status": "healthy"}}
''',
            'Dockerfile': '''FROM python:3.12-slim

WORKDIR /app
COPY requirements.txt .
RUN pip install --no-cache-dir -r requirements.txt

COPY app/ ./app/
EXPOSE 8000
CMD ["uvicorn", "app.main:app", "--host", "0.0.0.0", "--port", "8000"]
''',
            '.gitignore': '''__pycache__/
*.py[cod]
.venv/
.env
''',
        }
    },
    'cli': {
        'description': 'CLI tool with Click',
        'files': {
            'README.md': '# {name}\n\nA command-line tool.\n\n## Install\n\n```bash\npip install -e .\n```\n',
            'pyproject.toml': '''[project]
name = "{name}"
version = "0.1.0"
dependencies = ["click>=8.0.0", "rich>=13.0.0"]

[project.scripts]
{name} = "{name}.cli:main"
''',
            '{name}/__init__.py': '',
            '{name}/cli.py': '''"""CLI entry point."""

import click
from rich.console import Console

console = Console()


@click.group()
@click.version_option()
def main():
    """A helpful CLI tool."""
    pass


@main.command()
@click.argument("name", default="World")
def hello(name: str):
    """Say hello."""
    console.print(f"[green]Hello, {{name}}![/green]")


if __name__ == "__main__":
    main()
''',
            '.gitignore': '''__pycache__/
*.egg-info/
dist/
''',
        }
    }
}


@click.command()
@click.argument('template', type=click.Choice(list(TEMPLATES.keys())))
@click.argument('name')
@click.option('--path', '-p', default='.', help='Where to create project')
def new(template: str, name: str, path: str):
    """
    Create a new project from template.
    
    Templates: python, fastapi, cli
    """
    base_path = Path(path) / name
    
    if base_path.exists():
        console.print(f"[red]Error: {base_path} already exists[/red]")
        return
    
    template_data = TEMPLATES[template]
    console.print(f"Creating [cyan]{template_data['description']}[/cyan]: {name}")
    
    # Create files
    for file_path, content in template_data['files'].items():
        # Replace placeholders
        file_path = file_path.format(name=name)
        content = content.format(name=name)
        
        full_path = base_path / file_path
        full_path.parent.mkdir(parents=True, exist_ok=True)
        full_path.write_text(content)
        console.print(f"  [green]Created[/green] {file_path}")
    
    console.print(f"\n[bold green]✓ Project created![/bold green]")
    console.print(f"\nNext steps:")
    console.print(f"  cd {name}")
    
    if template == 'python':
        console.print("  python -m venv .venv")
        console.print("  source .venv/bin/activate")
        console.print("  pip install -e '.[dev]'")
    elif template == 'fastapi':
        console.print("  pip install -r requirements.txt")
        console.print("  uvicorn app.main:app --reload")
    elif template == 'cli':
        console.print("  pip install -e .")
        console.print(f"  {name} --help")
PreviousNext