abhijanfastapi 1.0.0__tar.gz

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -0,0 +1,75 @@
1
+ Metadata-Version: 2.5
2
+ Name: abhijanfastapi
3
+ Version: 1.0.0
4
+ Summary: Abhijan FastAPI scaffolding package with `abhijan init` and `abhijan alembic` CLI.
5
+ Project-URL: Homepage, https://github.com/Kilo-Org/kilocode
6
+ Project-URL: Issues, https://github.com/Kilo-Org/kilocode/issues
7
+ Author: Abhijan
8
+ License: MIT
9
+ Keywords: alembic,cli,fastapi,scaffolding
10
+ Classifier: Development Status :: 4 - Beta
11
+ Classifier: Environment :: Console
12
+ Classifier: Intended Audience :: Developers
13
+ Classifier: License :: OSI Approved :: MIT License
14
+ Classifier: Operating System :: OS Independent
15
+ Classifier: Programming Language :: Python :: 3
16
+ Classifier: Programming Language :: Python :: 3.10
17
+ Classifier: Programming Language :: Python :: 3.11
18
+ Classifier: Programming Language :: Python :: 3.12
19
+ Classifier: Topic :: Software Development :: Code Generators
20
+ Requires-Python: >=3.10
21
+ Requires-Dist: rich>=13
22
+ Requires-Dist: typer>=0.12
23
+ Description-Content-Type: text/markdown
24
+
25
+ # abhijanfastapi
26
+
27
+ Scaffolding CLI for Abhijan FastAPI projects.
28
+
29
+ Provides the `abhijan` command with two subcommands:
30
+
31
+ - `abhijan init` — creates `main.py`, `config.py`, `database.py`
32
+ - `abhijan alembic` — creates `alembic/env.py` with the async migration config
33
+
34
+ ## Install
35
+
36
+ ```bash
37
+ pip install abhijanfastapi
38
+ ```
39
+
40
+ ## Usage
41
+
42
+ ```bash
43
+ # scaffold a new project in the current directory
44
+ abhijan init
45
+
46
+ # scaffold into a specific directory
47
+ abhijan init -d ./my-app
48
+
49
+ # (re)create alembic/env.py
50
+ abhijan alembic
51
+
52
+ # overwrite existing files without prompting
53
+ abhijan init -f
54
+ ```
55
+
56
+ After scaffolding, run your app with:
57
+
58
+ ```bash
59
+ uvicorn main:app --reload
60
+ ```
61
+
62
+ And run migrations with:
63
+
64
+ ```bash
65
+ alembic revision --autogenerate -m "initial"
66
+ alembic upgrade head
67
+ ```
68
+
69
+ ## Requirements
70
+
71
+ - Python >= 3.10
72
+
73
+ ## License
74
+
75
+ MIT
@@ -0,0 +1,51 @@
1
+ # abhijanfastapi
2
+
3
+ Scaffolding CLI for Abhijan FastAPI projects.
4
+
5
+ Provides the `abhijan` command with two subcommands:
6
+
7
+ - `abhijan init` — creates `main.py`, `config.py`, `database.py`
8
+ - `abhijan alembic` — creates `alembic/env.py` with the async migration config
9
+
10
+ ## Install
11
+
12
+ ```bash
13
+ pip install abhijanfastapi
14
+ ```
15
+
16
+ ## Usage
17
+
18
+ ```bash
19
+ # scaffold a new project in the current directory
20
+ abhijan init
21
+
22
+ # scaffold into a specific directory
23
+ abhijan init -d ./my-app
24
+
25
+ # (re)create alembic/env.py
26
+ abhijan alembic
27
+
28
+ # overwrite existing files without prompting
29
+ abhijan init -f
30
+ ```
31
+
32
+ After scaffolding, run your app with:
33
+
34
+ ```bash
35
+ uvicorn main:app --reload
36
+ ```
37
+
38
+ And run migrations with:
39
+
40
+ ```bash
41
+ alembic revision --autogenerate -m "initial"
42
+ alembic upgrade head
43
+ ```
44
+
45
+ ## Requirements
46
+
47
+ - Python >= 3.10
48
+
49
+ ## License
50
+
51
+ MIT
@@ -0,0 +1,3 @@
1
+ """Abhijan FastAPI package - scaffolding utilities."""
2
+
3
+ __version__ = "1.0.0"
@@ -0,0 +1,118 @@
1
+ """CLI entry point: `abhijan init` and `abhijan alembic` scaffolding commands."""
2
+
3
+ from __future__ import annotations
4
+
5
+ from pathlib import Path
6
+
7
+ import typer
8
+ from rich.console import Console
9
+ from rich.prompt import Confirm
10
+
11
+ app = typer.Typer(name="abhijan", help="Abhijan FastAPI scaffolding CLI")
12
+ console = Console()
13
+
14
+ _TEMPLATES_DIR = Path(__file__).resolve().parent / "templates"
15
+
16
+ # Map template file -> destination filename
17
+ _TEMPLATES = {
18
+ "main.py.template": "main.py",
19
+ "config.py.template": "config.py",
20
+ "database.py.template": "database.py",
21
+ "alembic/env.py.template": "alembic/env.py",
22
+ }
23
+
24
+
25
+ def _write_template(
26
+ src_name: str,
27
+ dest_name: str,
28
+ directory: Path,
29
+ force: bool,
30
+ ) -> bool:
31
+ """Write a template file to the destination, prompting on conflicts."""
32
+ src = _TEMPLATES_DIR / src_name
33
+ dest = directory / dest_name
34
+ content = src.read_text()
35
+
36
+ if dest.exists() and not force:
37
+ if not Confirm.ask(
38
+ f"[yellow]{dest_name}[/yellow] already exists. Overwrite?",
39
+ default=False,
40
+ ):
41
+ console.print(f" [dim]- skipped {dest_name}[/dim]")
42
+ return False
43
+
44
+ dest.parent.mkdir(parents=True, exist_ok=True)
45
+ dest.write_text(content)
46
+ console.print(f" [green]+[/green] {dest_name}")
47
+ return True
48
+
49
+
50
+ @app.callback(invoke_without_command=True)
51
+ def _root(ctx: typer.Context) -> None:
52
+ if ctx.invoked_subcommand is None:
53
+ console.print(
54
+ "[bold green]abhijan[/bold green] - Abhijan FastAPI scaffolding tool.\n"
55
+ "Run [cyan]abhijan init[/cyan] to scaffold a new project, or "
56
+ "[cyan]abhijan alembic[/cyan] to (re)create alembic/env.py."
57
+ )
58
+
59
+
60
+ @app.command()
61
+ def init(
62
+ directory: Path = typer.Option(
63
+ Path("."),
64
+ "--directory",
65
+ "-d",
66
+ help="Target directory to scaffold into (default: current directory).",
67
+ ),
68
+ force: bool = typer.Option(
69
+ False,
70
+ "--force",
71
+ "-f",
72
+ help="Overwrite existing files without prompting.",
73
+ ),
74
+ ) -> None:
75
+ """Scaffold a new Abhijan FastAPI project."""
76
+ directory = directory.resolve()
77
+ directory.mkdir(parents=True, exist_ok=True)
78
+
79
+ console.print(f"[bold]Scaffolding Abhijan FastAPI project in[/bold] {directory}")
80
+
81
+ for src_name, dest_name in _TEMPLATES.items():
82
+ if dest_name.startswith("alembic/"):
83
+ continue
84
+ _write_template(src_name, dest_name, directory, force)
85
+
86
+ console.print("\n[bold green]Done.[/bold green] Run your app with:")
87
+ console.print(" [cyan]uvicorn main:app --reload[/cyan]")
88
+
89
+
90
+ @app.command()
91
+ def alembic(
92
+ directory: Path = typer.Option(
93
+ Path("."),
94
+ "--directory",
95
+ "-d",
96
+ help="Target directory (default: current directory).",
97
+ ),
98
+ force: bool = typer.Option(
99
+ False,
100
+ "--force",
101
+ "-f",
102
+ help="Overwrite existing alembic/env.py without prompting.",
103
+ ),
104
+ ) -> None:
105
+ """(Re)create alembic/env.py with the Abhijan async migration config."""
106
+ directory = directory.resolve()
107
+ directory.mkdir(parents=True, exist_ok=True)
108
+
109
+ console.print(f"[bold]Writing alembic/env.py in[/bold] {directory}")
110
+ _write_template("alembic/env.py.template", "alembic/env.py", directory, force)
111
+
112
+ console.print("\n[bold green]Done.[/bold green] Then run:")
113
+ console.print(" [cyan]alembic revision --autogenerate -m 'initial'[/cyan]")
114
+ console.print(" [cyan]alembic upgrade head[/cyan]")
115
+
116
+
117
+ if __name__ == "__main__":
118
+ app()
@@ -0,0 +1,68 @@
1
+ from __future__ import annotations
2
+
3
+ import asyncio
4
+ from logging.config import fileConfig
5
+
6
+ from sqlalchemy import pool
7
+ from sqlalchemy.engine import Connection
8
+ from sqlalchemy.ext.asyncio import async_engine_from_config
9
+
10
+ from alembic import context
11
+
12
+ from config import settings
13
+ from database import Base
14
+ from models import Todo # noqa: F401 - required for autogenerate
15
+
16
+ config = context.config
17
+
18
+ if config.config_file_name is not None:
19
+ fileConfig(config.config_file_name)
20
+
21
+ target_metadata = Base.metadata
22
+
23
+ config.set_main_option("sqlalchemy.url", settings.database_url)
24
+
25
+
26
+ def run_migrations_offline() -> None:
27
+ """Run migrations in 'offline' mode."""
28
+ url = config.get_main_option("sqlalchemy.url")
29
+ context.configure(
30
+ url=url,
31
+ target_metadata=target_metadata,
32
+ literal_binds=True,
33
+ dialect_opts={"paramstyle": "named"},
34
+ )
35
+
36
+ with context.begin_transaction():
37
+ context.run_migrations()
38
+
39
+
40
+ def do_run_migrations(connection: Connection) -> None:
41
+ context.configure(
42
+ connection=connection,
43
+ target_metadata=target_metadata,
44
+ compare_type=True,
45
+ )
46
+
47
+ with context.begin_transaction():
48
+ context.run_migrations()
49
+
50
+
51
+ async def run_migrations_online() -> None:
52
+ """Run migrations in 'online' mode."""
53
+ connectable = async_engine_from_config(
54
+ config.get_section(config.config_ini_section, {}),
55
+ prefix="sqlalchemy.",
56
+ poolclass=pool.NullPool,
57
+ )
58
+
59
+ async with connectable.connect() as connection:
60
+ await connection.run_sync(do_run_migrations)
61
+
62
+ await connectable.dispose()
63
+
64
+
65
+ if context.is_offline_mode():
66
+ run_migrations_offline()
67
+ else:
68
+ asyncio.run(run_migrations_online())
@@ -0,0 +1,11 @@
1
+ from pydantic_settings import BaseSettings, SettingsConfigDict
2
+
3
+
4
+ class Settings(BaseSettings):
5
+ database_url: str = "postgresql+asyncpg://user:pass@localhost:5432/abhijanfastapi"
6
+ debug: bool = False
7
+
8
+ model_config = SettingsConfigDict(env_file=".env", env_prefix="")
9
+
10
+
11
+ settings = Settings()
@@ -0,0 +1,38 @@
1
+ from sqlalchemy.ext.asyncio import AsyncSession, create_async_engine
2
+ from sqlalchemy.orm import DeclarativeBase, sessionmaker
3
+
4
+ from config import settings
5
+
6
+ engine = create_async_engine(
7
+ settings.database_url,
8
+ echo=settings.debug,
9
+ future=True,
10
+ )
11
+
12
+ AsyncSessionLocal = sessionmaker(
13
+ bind=engine,
14
+ class_=AsyncSession,
15
+ expire_on_commit=False,
16
+ autoflush=False,
17
+ )
18
+
19
+
20
+ class Base(DeclarativeBase):
21
+ pass
22
+
23
+
24
+ async def get_db() -> AsyncSession:
25
+ async with AsyncSessionLocal() as session:
26
+ try:
27
+ yield session
28
+ await session.commit()
29
+ except Exception:
30
+ await session.rollback()
31
+ raise
32
+ finally:
33
+ await session.close()
34
+
35
+
36
+ async def init_db() -> None:
37
+ async with engine.begin() as conn:
38
+ await conn.run_sync(Base.metadata.create_all)
@@ -0,0 +1,36 @@
1
+ from fastapi import FastAPI
2
+ from fastapi.middleware.cors import CORSMiddleware
3
+
4
+ from database import init_db
5
+
6
+ app = FastAPI(title="Abhijan FastAPI", version="1.0.0")
7
+
8
+ origins = [
9
+ "http://localhost:3000",
10
+ "http://localhost:5173",
11
+ "http://127.0.0.1:3000",
12
+ "http://127.0.0.1:5173",
13
+ ]
14
+
15
+ app.add_middleware(
16
+ CORSMiddleware,
17
+ allow_origins=origins,
18
+ allow_credentials=True,
19
+ allow_methods=["*"],
20
+ allow_headers=["*"],
21
+ )
22
+
23
+
24
+ @app.get("/")
25
+ def read_root():
26
+ return {"message": "Hello from Abhijan FastAPI"}
27
+
28
+
29
+ @app.get("/health")
30
+ def health_check():
31
+ return {"status": "ok"}
32
+
33
+
34
+ @app.on_event("startup")
35
+ async def on_startup() -> None:
36
+ await init_db()
@@ -0,0 +1,40 @@
1
+ [build-system]
2
+ requires = ["hatchling"]
3
+ build-backend = "hatchling.build"
4
+
5
+ [project]
6
+ name = "abhijanfastapi"
7
+ version = "1.0.0"
8
+ description = "Abhijan FastAPI scaffolding package with `abhijan init` and `abhijan alembic` CLI."
9
+ readme = "README.md"
10
+ requires-python = ">=3.10"
11
+ license = { text = "MIT" }
12
+ authors = [{ name = "Abhijan" }]
13
+ keywords = ["fastapi", "scaffolding", "alembic", "cli"]
14
+ classifiers = [
15
+ "Development Status :: 4 - Beta",
16
+ "Environment :: Console",
17
+ "Intended Audience :: Developers",
18
+ "License :: OSI Approved :: MIT License",
19
+ "Operating System :: OS Independent",
20
+ "Programming Language :: Python :: 3",
21
+ "Programming Language :: Python :: 3.10",
22
+ "Programming Language :: Python :: 3.11",
23
+ "Programming Language :: Python :: 3.12",
24
+ "Topic :: Software Development :: Code Generators",
25
+ ]
26
+ dependencies = [
27
+ "typer>=0.12",
28
+ "rich>=13",
29
+ ]
30
+
31
+ [project.scripts]
32
+ abhijan = "fastapipkg.cli:app"
33
+
34
+ [project.urls]
35
+ Homepage = "https://github.com/Kilo-Org/kilocode"
36
+ Issues = "https://github.com/Kilo-Org/kilocode/issues"
37
+
38
+ [tool.hatch.build.targets.wheel]
39
+ packages = ["fastapipkg"]
40
+ dev-mode-dirs = ["."]