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.
- abhijanfastapi-1.0.0/PKG-INFO +75 -0
- abhijanfastapi-1.0.0/README.md +51 -0
- abhijanfastapi-1.0.0/fastapipkg/__init__.py +3 -0
- abhijanfastapi-1.0.0/fastapipkg/cli.py +118 -0
- abhijanfastapi-1.0.0/fastapipkg/templates/alembic/env.py.template +68 -0
- abhijanfastapi-1.0.0/fastapipkg/templates/config.py.template +11 -0
- abhijanfastapi-1.0.0/fastapipkg/templates/database.py.template +38 -0
- abhijanfastapi-1.0.0/fastapipkg/templates/main.py.template +36 -0
- abhijanfastapi-1.0.0/pyproject.toml +40 -0
|
@@ -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,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 = ["."]
|