Skip to content

Commit 4146f35

Browse files
Merge pull request albertalexandrov#4 from albertalexandrov/commands-1
новый механизм работы с командами
2 parents f053bc1 + 86f72bb commit 4146f35

File tree

9 files changed

+95
-60
lines changed

9 files changed

+95
-60
lines changed
438 KB
Loading
358 KB
Loading

docs/Команды.md

Lines changed: 70 additions & 35 deletions
Original file line numberDiff line numberDiff line change
@@ -1,59 +1,94 @@
1+
from fastapi_django.conf.global_settings import MANAGEMENT
2+
13
# Команды
24

35
Работа с командами реализована в модуле [management](../fastapi_django/management). Работают команды на [Typer](https://typer.tiangolo.com)
46

5-
В модуле [management/commands](../fastapi_django/management/commands) реализованы дефолтные команды runserver и echo.
7+
## CLI
8+
9+
Экземпляр Typer и команды созданы в модуле [management/cli.py](../fastapi_django/management/cli.py). Из-за особенностей работы с Typer необходимо, чтобы
10+
интерпретатор Python при импорте экземпляра Typer проходил также команды, обернутые декоратором `command()`.
11+
12+
## Дефолтные команды
13+
14+
Реализованы дефолтные команды `echo` и `runserver` (см. [management/cli.py](../fastapi_django/management/cli.py)).
15+
16+
## Директория management/commands
617

7-
По сути manage.py является прокси к приложению на Typer.
18+
Дефолтные команды `echo` и `runserver` простые и вполне помещаются в функцию. Однако, если логика громоздкая, то возникает
19+
потребность поместить ее в класс. В таком случае, предлагается размещать в каждый свой файл как это например показано
20+
по ссылке https://github.com/albertalexandrov/fastapi-django-example/blob/main/src/management/commands/calculator.py.
21+
Осталось эту логику запустить. Для этого в https://github.com/albertalexandrov/fastapi-django-example/blob/main/src/management/cli.py создается команда,
22+
которая запускает ее.
823

924
## Кастомные команды
1025

11-
Команда - это callable объект, который может находится в любом месте проекта. Но чтобы команда разаработала, ее необходимо
12-
зарегистрировать. Предлагается регистрировать ее в том же модуле, в котором вызывается приложение Typer, то есть в модуле manage.py
13-
при помоши функции management.utils.register_command.
26+
Объект `typer` [management/cli.py](../fastapi_django/management/cli.py) является **корневым** CLI, в которому должны
27+
добавляться кастомные команды.
1428

15-
Пример кастомной команды https://github.com/albertalexandrov/fastapi-django-example/tree/main/src/commands.
16-
Пример регистрации команды: https://github.com/albertalexandrov/fastapi-django-example/blob/main/src/manage.py#L10
29+
Вне зависимости от того, идет ли речь про код приложения FastAPI или некоторой библиотеки предлагается следующий алгоритм
30+
создания кастомных команд.
1731

18-
## Команда runserver
32+
1. В проекте/библиотеке создается структура папок и файлов:
1933

20-
Данная команда запускает приложение FastAPI при помощи Uvicorn. Если взглянуть на ее реализацию, можно увидеть,
21-
как она понимает, с какими параметрами необходимо запустить приложение:
34+
```python
35+
management
36+
├── commands (опционально)
37+
├── __init__.py
38+
├── cli.py
39+
```
40+
41+
2. в `cli.py` определяется, настраивается объект `Typer`, реализуются кастомные команды:
2242

2343
```python
24-
def runserver() -> None:
25-
params = {}
26-
uvicorn_settings = [setting for setting in dir(settings) if setting.startswith("UVICORN_")]
27-
for setting in uvicorn_settings:
28-
_, param = setting.split("_", 1)
29-
params[param.lower()] = getattr(settings, setting)
30-
uvicorn.run(**params)
44+
# cli.py
45+
46+
from typer import Typer
47+
48+
typer = Typer(help="Команды проекта/библиотеки")
49+
50+
51+
@typer.command()
52+
def info():
53+
print("Вот информация о проекте/библиотеке")
54+
3155
```
3256

33-
Параметры запуска Uvicorn задаются в settings. Названия параметров должны иметь префикс UVICORN_
34-
Далее идут названия параметров функции uvicorn.run в верхнем регистре. Таким образом, параметр,
35-
соответствующий параметру workers будет иметь название UVICORN_WORKERS, для port - UVICORN_PORT и тд
57+
3. Подключение к корневому CLI
3658

37-
## Список команд
59+
Осталось подключить созданный объект typer, который содержит кастомные команды, к корневому CLI.
60+
Для этого необходимо в настройку MANAGEMENT прописать:
3861

39-
Здесь https://github.com/albertalexandrov/fastapi-django-example/blob/main/src/manage.py#L10 была зарегистрирована
40-
кастомная команда. Убедится, команда была зарегистрирована можно введя в консоль список всех команд командой:
62+
```python
63+
# settings.py
4164

42-
```shell
43-
python manage.py --help
65+
MANAGEMENT = [
66+
{
67+
"typer": "management.cli:typer",
68+
}
69+
]
4470
```
4571

46-
![](assets/images/commands-list.png)
72+
4. Проверка
4773

48-
## Примечание
74+
Чтобы просмотреть список команд, необходимо выполнить команду `python manage.py --help`:
4975

50-
Я рассчитывал, что объект [cli](../fastapi_django/management/__init__.py) будет импортироваться где нужно и
51-
команды будут автоматически регистрироваться. Однако, недостаточно обернуть функцию в декоратор cli.command,
52-
поэтому приходится делать так, что команда регистрируется аккурат перед тем, когда выполняется
53-
cli() ([в файле manage.py](https://github.com/albertalexandrov/fastapi-django-example/blob/main/src/manage.py#L10))
76+
![](assets/images/custom-command.png)
5477

55-
Поэтому сторонние библиотеки (не проект) не смогут просто оборачивать свои собственные команды - в проекте придется
56-
импортировать команды сторонних библиотек и регистрировать.
78+
Можно видеть, что отобразились как дефолтные команды, так и кастомная команд.
79+
80+
Если в настройке MANAGEMENT определить name, то команды будут объединены в субкоманды, доступ к которым будет через
81+
name:
82+
83+
```python
84+
# settings.py
85+
86+
MANAGEMENT = [
87+
{
88+
"typer": "management.cli:typer",
89+
"name": "custom-commands",
90+
}
91+
]
92+
```
5793

58-
**Возможно у кого нибудь возникнут идеи как реализовать автоматическую регистрацию команд. Автодисковеринг как в Celery
59-
или типа того. Пишите.**
94+
![](assets/images/custom-command-under-name.png)

fastapi_django/conf/global_settings.py

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -27,3 +27,5 @@
2727
# # другие параметры, которые будут переданы как kw в функцию create_async_engine()
2828
# }
2929
# }
30+
31+
MANAGEMENT: list[dict] = []
Lines changed: 7 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -1,12 +1,11 @@
1-
from collections.abc import Callable
2-
31
from typer import Typer
2+
from fastapi_django.conf import settings
3+
from uvicorn.importer import import_from_string
44

5-
from fastapi_django.management.commands.echo import echo
6-
from fastapi_django.management.commands.runserver import runserver
5+
typer = Typer(rich_markup_mode="markdown")
6+
management = [{"typer": "fastapi_django.management.cli:typer"}] + settings.MANAGEMENT
77

8-
cli = Typer()
9-
commands: list[Callable] = [echo, runserver]
8+
for item in management:
9+
typer.add_typer(import_from_string(item["typer"]), name=item.get("name"))
1010

11-
for command in commands:
12-
cli.command()(command)
11+
__all__ = ["typer"]
Lines changed: 15 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,14 +1,18 @@
11
import uvicorn
2+
from typer import Typer
23

34
from fastapi_django.conf import settings
45

6+
typer = Typer(rich_markup_mode="markdown")
57

6-
def runserver() -> None:
8+
9+
@typer.command() # TODO: можно определить rich_help_panel, которые действуют объединяюще как теги в сваггере
10+
def runserver():
711
"""
812
Запускает приложение при помощи Uvicorn.
913
10-
Параметры запуска Uvicorn задаются в settings. Названия параметров должны иметь префикс UVICORN_
11-
Далее идут названия параметров функции uvicorn.run в верхнем регистре. Таким образом, параметр,
14+
Параметры запуска Uvicorn задаются в settings. Названия параметров должны иметь префикс UVICORN_
15+
Далее идут названия параметров функции uvicorn.run в верхнем регистре. Таким образом, параметр,
1216
соответствующий параметру workers будет иметь название UVICORN_WORKERS, для port - UVICORN_PORT и тд
1317
"""
1418
params = {}
@@ -17,3 +21,11 @@ def runserver() -> None:
1721
_, param = setting.split("_", 1)
1822
params[param.lower()] = getattr(settings, setting)
1923
uvicorn.run(**params)
24+
25+
26+
@typer.command()
27+
def echo(message: str):
28+
"""
29+
Эхо-команда
30+
"""
31+
print(f"Echo: {message}")

fastapi_django/management/commands/echo.py

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

fastapi_django/management/utils.py

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

pyproject.toml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -7,7 +7,7 @@ check_untyped_defs = true
77

88
[tool.poetry]
99
name = "fastapi-django"
10-
version = "0.8.28"
10+
version = "0.9.12"
1111
description = ""
1212
authors = ["albertalexandrov <[email protected]>"]
1313
packages = [{include = "fastapi_django"}]

0 commit comments

Comments
 (0)