Skip to content

Commit

Permalink
Merge pull request #3 from aarcex3/main
Browse files Browse the repository at this point in the history
refactor: improve cli
  • Loading branch information
ch-iv authored Sep 17, 2024
2 parents f51df08 + 0831c8e commit 9407f90
Show file tree
Hide file tree
Showing 32 changed files with 317 additions and 47 deletions.
2 changes: 2 additions & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -160,3 +160,5 @@ cython_debug/
# and can be added to the global gitignore or merged into this file. For a more nuclear
# option (not recommended) you can uncomment the following to ignore the entire idea folder.
.idea/
.vscode/
.ruff_cache/
25 changes: 25 additions & 0 deletions Makefile
Original file line number Diff line number Diff line change
@@ -0,0 +1,25 @@
# Variables
PYTHON := pdm run python
PYTEST := pdm run pytest
RUFF := pdm run ruff format
FIND := find

# Directories
SRC_DIR := src
TEST_DIR := tests

# Targets
.PHONY: clean test run


#Format the files
format:
$(RUFF) $(SRC_DIR)
$(RUFF) $(TEST_DIR)

# Clean target to delete __pycache__ directories
clean:
$(FIND) . -type d -name "__pycache__" -exec rm -rf {} +

test:
$(PYTEST) $(TEST_DIR) -vv -s --showlocals
4 changes: 2 additions & 2 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -7,13 +7,13 @@ project using the Litestar CLI.

When the litestar-manage module is installed, it will extend the Litestar native CLI. To create a starter project, run the following command:

```
```bash
litestar project init --app-name MyProject
```

This command is used to initialize a Litestar project named MyProject in the current working directory. MyProject will have the following tree structure:

```
```bash
app/
├─ templates/
│ ├─ index.html
Expand Down
61 changes: 56 additions & 5 deletions pdm.lock

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

2 changes: 2 additions & 0 deletions pyproject.toml
Original file line number Diff line number Diff line change
Expand Up @@ -150,6 +150,8 @@ lint.ignore = [
"PT",
"TD",
"PERF203", # ignore for now; investigate
"COM812",
"ISC001"
]
lint.select = ["ALL"]
# Allow unused variables when underscore-prefixed.
Expand Down
4 changes: 2 additions & 2 deletions src/litestar_manage/__main__.py
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,7 @@ def run_cli() -> None:
"""Application Entrypoint."""
import sys

from litestar_manage.cli import project_group
from litestar_manage.cli import project

try:
from litestar.__main__ import run_cli as run_litestar_cli
Expand All @@ -26,7 +26,7 @@ def run_cli() -> None:
print(exc) # noqa: T201
sys.exit(1)
else:
litestar_group.add_command(cast(RichCommand, project_group))
litestar_group.add_command(cast(RichCommand, project))
run_litestar_cli()


Expand Down
48 changes: 39 additions & 9 deletions src/litestar_manage/cli.py
Original file line number Diff line number Diff line change
Expand Up @@ -6,16 +6,22 @@
from click import group, option
from litestar.cli._utils import LitestarGroup

from litestar_manage.renderer import RenderingContext, render_template
from litestar_manage.constants import VENV_NAME
from litestar_manage.renderer import AppRenderingContext, ResourceRenderingContext, render_template
from litestar_manage.venv_builder import PipVenvBuilder, init_venv


@group(cls=LitestarGroup, name="project")
def project_group() -> None:
"""Manage Scaffolding Tasks."""
def is_project_initialized() -> bool:
output_dir = Path.cwd()
return (output_dir / "src").exists() or (output_dir / "venv").exists()


@click.group(cls=LitestarGroup)
def project():
pass

@project_group.command(name="init", help="Initialize a new Litestar project.")

@project.command(name="new", help="Initialize a new Litestar project.")
@option(
"--app-name",
type=str,
Expand All @@ -31,14 +37,38 @@ def project_group() -> None:
def init_project(app_name: str, venv: str | None) -> None:
"""CLI command to initialize a Litestar project"""

template_dir = Path(__file__).parent / "template"
template_dir = Path(__file__).parent / "templates" / "app"
output_dir = Path.cwd()
ctx = RenderingContext(app_name=app_name)

if is_project_initialized():
click.echo("Project already initialized.")
return

ctx = AppRenderingContext(app_name=app_name)
render_template(template_dir, output_dir, ctx, run_ruff=True)

packages_to_install = ["litestar"]
venv_name = "venv"
if venv == "pip":
builder = PipVenvBuilder()
init_venv(output_dir / venv_name, builder, packages_to_install)
init_venv(output_dir / VENV_NAME, builder, packages_to_install)


@project.command(name="resource")
@option(
"--resource-name",
"-n",
type=str,
required=True,
)
def generate_resource(resource_name: str) -> None:
"""CLI command to generate a new resource (controller, service, dto, models, repository)"""

if not is_project_initialized():
click.echo("Project not initialized. Please initialize the project first.")
return

template_dir = Path(__file__).parent / "templates" / "resource"
output_dir = Path.cwd() / "src" / f"{resource_name.lower()}"
ctx = ResourceRenderingContext(resource_name=resource_name)

render_template(template_dir, output_dir, ctx, run_ruff=True)
1 change: 1 addition & 0 deletions src/litestar_manage/constants.py
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
VENV_NAME = ".venv"
16 changes: 12 additions & 4 deletions src/litestar_manage/renderer.py
Original file line number Diff line number Diff line change
Expand Up @@ -7,14 +7,15 @@
import sysconfig
from dataclasses import dataclass
from pathlib import Path
from typing import Union

from jinja2 import Template


def render_template(
template_dir: Path,
output_dir: Path,
ctx: RenderingContext,
ctx: Union[AppRenderingContext, ResourceRenderingContext],
run_ruff: bool = True,
) -> None:
"""Renders a template from template_dir to output_dir using the provided context.
Expand All @@ -36,16 +37,23 @@ def render_template(


@dataclass
class RenderingContext:
class AppRenderingContext:
"""Context for rendering an application template."""

app_name: str


@dataclass
class ResourceRenderingContext:
"""Context for rendering resource (controller, service, dto, model and repository)."""

resource_name: str


def _render_jinja_dir(
input_directory: Path,
output_directory: Path,
ctx: RenderingContext,
ctx: Union[AppRenderingContext, ResourceRenderingContext],
) -> list[Path]:
"""Recursively renders all files in the input directory to the output directory,
while preserving the file-tree structure. Returns the list of paths to the created files.
Expand Down Expand Up @@ -101,7 +109,7 @@ def find_ruff_bin() -> Path:
if scripts_path.is_file():
return scripts_path

if sys.version_info >= (3, 10): # noqa: UP036
if sys.version_info >= (3, 10): # noqa: UP036
user_scheme = sysconfig.get_preferred_scheme("user")
elif os.name == "nt":
user_scheme = "nt_user"
Expand Down
12 changes: 0 additions & 12 deletions src/litestar_manage/template/app/controllers/web.py

This file was deleted.

File renamed without changes.
18 changes: 18 additions & 0 deletions src/litestar_manage/templates/app/src/app_controller.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,18 @@
from typing import Dict

from litestar import Controller, get
from litestar.di import Provide
from litestar.enums import MediaType
from litestar.status_codes import HTTP_200_OK
from src.app_service import AppService, provide_app_service


class AppController(Controller):
"""App controller."""

dependencies = {"app_service": Provide(provide_app_service)}

@get(path="/", status_code=HTTP_200_OK, media_type=MediaType.JSON)
async def index(self, app_service: AppService) -> Dict:
"""App index"""
return await app_service.app_info()
16 changes: 16 additions & 0 deletions src/litestar_manage/templates/app/src/app_service.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
from typing import Dict


class AppService:
"""App Service"""

app_name = ""
app_version = "0.1.0"

async def app_info(self) -> Dict[str, str]:
"""Return info about the app"""
return {"app_name": self.app_name, "verion": self.app_version}


def provide_app_service():
return AppService()
File renamed without changes.
Original file line number Diff line number Diff line change
Expand Up @@ -18,15 +18,15 @@ def create_app() -> Litestar:
from litestar.static_files import create_static_files_router
from litestar.template.config import TemplateConfig

from app.config import assets_path, templates_path
from app.controllers.web import WebController
from .app_controller import AppController
from .config import assets_path, templates_path

logging_middleware_config = LoggingMiddlewareConfig()
session_config = CookieBackendConfig(secret=urandom(16))

return Litestar(
route_handlers=[
WebController,
AppController,
create_static_files_router(path="/static", directories=[assets_path]),
],
middleware=[session_config.middleware, logging_middleware_config.middleware],
Expand Down
Empty file.
6 changes: 6 additions & 0 deletions src/litestar_manage/templates/resource/controller.py.jinja2
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
from litestar.controller import Controller


class {{resource_name.capitalize()}}Controller(Controller):
"""{{resource_name.capitalize()}} Controller"""
pass
10 changes: 10 additions & 0 deletions src/litestar_manage/templates/resource/dto.py.jinja2
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
from advanced_alchemy.extensions.litestar.dto import SQLAlchemyDTO, SQLAlchemyDTOConfig

from src.{{resource_name.lower()}}.models import {{resource_name.capitalize()}}


class {{resource_name.capitalize()}}DTO(SQLAlchemyDTO[{{resource_name.capitalize()}}]):
"""
{{resource_name.capitalize()}} DTO
"""
config = SQLAlchemyDTOConfig()
Loading

0 comments on commit 9407f90

Please sign in to comment.