Skip to content

Commit 4d9a989

Browse files
authored
Add lazy commands (#51)
* Add lazy commands * Add Lazy group
1 parent b9f9b1e commit 4d9a989

File tree

13 files changed

+162
-112
lines changed

13 files changed

+162
-112
lines changed

.dev/lint

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,14 +1,18 @@
11
#!/usr/bin/env bash
22
set -e
33

4+
ISORT_ARGS="-c"
45
BLACK_ARG="--check"
56
RUFF_ARG=""
67

78
if [ "$1" = "fix" ] ; then
9+
ISORT_ARGS=""
810
BLACK_ARG=""
911
RUFF_ARG="--fix"
1012
fi
1113

14+
echo isort
15+
isort fluid tests examples ${ISORT_ARGS}
1216
echo "run black"
1317
black fluid tests examples ${BLACK_ARG}
1418
echo "run ruff"

examples/__main__.py

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,12 @@
1+
import dotenv
2+
3+
dotenv.load_dotenv()
4+
5+
from fluid.scheduler.cli import TaskManagerCLI # isort:skip # noqa: E402
6+
7+
task_manager_cli = TaskManagerCLI(
8+
"examples.tasks:task_app", lazy_subcommands={"db": "examples.db.cli:cli"}
9+
)
10+
11+
12+
task_manager_cli()

examples/db/cli.py

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,3 @@
1+
from examples.db import get_db
2+
3+
cli = get_db().cli()

examples/db/migrations/versions/1f91ed2b2f26_initial.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
11
"""initial
22
33
Revision ID: 1f91ed2b2f26
4-
Revises:
4+
Revises:
55
Create Date: 2024-08-18 10:19:38.372777
66
77
"""

examples/main_db.py

Lines changed: 0 additions & 10 deletions
This file was deleted.

examples/main_tasks.py

Lines changed: 0 additions & 7 deletions
This file was deleted.

fluid/__init__.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,3 @@
11
"""Reusable server side python modules"""
22

3-
__version__ = "1.2.2"
3+
__version__ = "1.2.3"

fluid/scheduler/cli.py

Lines changed: 7 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -10,6 +10,7 @@
1010
from uvicorn.importer import import_from_string
1111

1212
from fluid.utils import log
13+
from fluid.utils.lazy import LazyGroup
1314

1415
if TYPE_CHECKING:
1516
from .consumer import TaskManager
@@ -19,8 +20,12 @@
1920
TaskManagerApp = FastAPI | Callable[..., Any] | str
2021

2122

22-
class TaskManagerCLI(click.Group):
23-
def __init__(self, task_manager_app: TaskManagerApp, **kwargs: Any):
23+
class TaskManagerCLI(LazyGroup):
24+
def __init__(
25+
self,
26+
task_manager_app: TaskManagerApp,
27+
**kwargs: Any,
28+
):
2429
kwargs.setdefault("commands", DEFAULT_COMMANDS)
2530
super().__init__(**kwargs)
2631
self.task_manager_app = task_manager_app

fluid/scheduler/crontab.py

Lines changed: 1 addition & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,4 @@
1-
"""Originally from https://github.com/coleifer/huey
2-
"""
1+
"""Originally from https://github.com/coleifer/huey"""
32

43
import re
54
from abc import ABC, abstractmethod

fluid/utils/lazy.py

Lines changed: 41 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,41 @@
1+
from importlib import import_module
2+
from typing import Any
3+
4+
import click
5+
6+
7+
class LazyGroup(click.Group):
8+
def __init__(
9+
self,
10+
*,
11+
lazy_subcommands: dict[str, str] | None = None,
12+
**kwargs: Any,
13+
):
14+
super().__init__(**kwargs)
15+
self.lazy_subcommands = lazy_subcommands or {}
16+
17+
def list_commands(self, ctx: click.Context) -> list[str]:
18+
commands = super().list_commands(ctx)
19+
commands.extend(self.lazy_subcommands)
20+
return sorted(commands)
21+
22+
def get_command(self, ctx: click.Context, cmd_name: str) -> click.Command | None:
23+
if cmd_name in self.lazy_subcommands:
24+
return self._lazy_load(cmd_name)
25+
return super().get_command(ctx, cmd_name)
26+
27+
def _lazy_load(self, cmd_name: str) -> click.Command:
28+
# lazily loading a command, first get the module name and attribute name
29+
import_path = self.lazy_subcommands[cmd_name]
30+
modname, cmd_object_name = import_path.rsplit(":", 1)
31+
# do the import
32+
mod = import_module(modname)
33+
# get the Command object from that module
34+
cmd_object = getattr(mod, cmd_object_name)
35+
# check the result to make debugging easier
36+
if not isinstance(cmd_object, click.Command):
37+
raise ValueError(
38+
f"Lazy loading of {import_path} failed by returning "
39+
"a non-command object"
40+
)
41+
return cmd_object

0 commit comments

Comments
 (0)