Skip to content

Commit 8459b29

Browse files
committed
Updated Command docs
1 parent b6c32bc commit 8459b29

File tree

6 files changed

+158
-129
lines changed

6 files changed

+158
-129
lines changed

docs/cli/click.md

Whitespace-only changes.

docs/cli/command-grouping.md

Lines changed: 0 additions & 55 deletions
Original file line numberDiff line numberDiff line change
@@ -1,55 +0,0 @@
1-
# **Command Grouping**
2-
Ella CLI provides a way by which commands can be grouped.
3-
4-
For instance, a `db` command may have sub-commands like `makemigrations`, `migrate`, `reset-db` etc.
5-
6-
To achieve this use-case, let us create a file `commands.py` in the root level of the project.
7-
8-
```python
9-
from ellar.common import EllarTyper
10-
11-
db = EllarTyper(name="db")
12-
13-
14-
@db.command(name="make-migrations")
15-
def makemigrations():
16-
"""Create DB Migration """
17-
18-
@db.command()
19-
def migrate():
20-
"""Applies Migrations"""
21-
```
22-
23-
## **Register EllarTyper Command**
24-
25-
Lets, make the `db` visible on the CLI.
26-
27-
In other for Ellar CLI to identify custom command, its has to be registered to a `@Module` class.
28-
29-
```python
30-
from ellar.common import Module
31-
from ellar.core import ModuleBase
32-
from .commands import db
33-
34-
@Module(commands=[db])
35-
class ApplicationModule(ModuleBase):
36-
pass
37-
```
38-
39-
open your terminal and navigate to project directory and run the command below
40-
```shell
41-
ellar db --help
42-
```
43-
44-
command output
45-
```shell
46-
Usage: Ellar, Python Web framework db [OPTIONS] COMMAND [ARGS]...
47-
48-
Options:
49-
--help Show this message and exit.
50-
51-
Commands:
52-
make-migrations Create DB Migration
53-
migrate Applies Migrations
54-
55-
```

docs/cli/custom-commands.md

Lines changed: 153 additions & 72 deletions
Original file line numberDiff line numberDiff line change
@@ -1,111 +1,192 @@
11
# **Custom Commands**
2-
In this section, we are going to go over how to create a custom command and throw more light on how Ella CLI works.
2+
In this section, we will guide you through the process of creating a command and explain why you
3+
should utilize `ellar_cli.click` as your primary Click package when working with Ellar.
4+
5+
6+
## **Command Options Arguments**
7+
The `ellar_cli.click` package offers comprehensive help messages and documentation for command options and arguments
8+
without compromising the fundamental functionality of the command.
9+
For instance:
10+
11+
```python
12+
import ellar_cli.click as click
13+
14+
@click.command()
15+
@click.argument("arg1", required=True, help="Arg1 description")
16+
@click.argument("arg2", required=False, help="Arg2 description")
17+
@click.option("-op1", required=False, help="option1 description")
18+
@click.option("-op2", required=False, help="option2 description")
19+
def my_command(arg1, arg2, op1, op2):
20+
print(f"ARG1={arg1} ARG2={arg2}; op1={op1} op2={op2}")
21+
```
22+
23+
When you register `my_command` to a `@Module` class, you can then execute it in your terminal with the following command:
24+
```shell
25+
ellar my-command --help
26+
27+
## OUTPUT
28+
29+
Usage: ellar my-command <arg1> [<arg2>] [OPTIONS]
30+
31+
ARGUMENTS:
32+
arg1: <arg1> Arg1 description [required]
33+
arg2: [<arg2>] Arg2 description
34+
35+
[OPTIONS]:
36+
-op1 TEXT option1 description
37+
-op2 TEXT option2 description
38+
--help Show this message and exit.
39+
```
40+
41+
## **With App Context Decorator**
42+
The `ellar_cli.click` module includes a command decorator function called `with_app_context`.
43+
This decorator ensures that a click command is executed within the application context,
44+
allowing `current_app`, `current_injector`, and `current_config` to have values.
45+
46+
For instance:
47+
48+
```python
49+
import ellar_cli.click as click
50+
from ellar.core import Config
51+
from ellar.app import current_injector
52+
53+
@click.command()
54+
@click.argument("arg1", required=True, help="Arg1 description")
55+
@click.with_app_context
56+
def command_context(arg1):
57+
config = current_injector.get(Config)
58+
print("ALLOWED_HOSTS:", config.ALLOWED_HOSTS, ";ELLAR_CONFIG_MODULE:", config.config_module)
59+
60+
@click.command()
61+
@click.argument("arg1", required=True, help="Arg1 description")
62+
def command_wc(arg1):
63+
config = current_injector.get(Config)
64+
print("ALLOWED_HOSTS:", config.ALLOWED_HOSTS, ";ELLAR_CONFIG_MODULE:", config.config_module)
65+
```
66+
67+
In this example, `command_context` is wrapped with `with_app_context`, while `command_wc` is not.
68+
When executing both commands, `command_context` will run successfully, and `command_wc` will raise a RuntimeError
69+
because it attempts to access a value outside the context.
70+
71+
## **AppContextGroup**
72+
`AppContextGroup` extended from `click.Group` to wrap all its commands with `with_app_context` decorator.
373

4-
## **Create Custom Command**
5-
Let's create a file called `commands.py` at the root level of the project.
674

775
```python
8-
# project_name/commands.py
76+
import ellar_cli.click as click
77+
from ellar.core import Config
78+
from ellar.app import current_injector
79+
80+
cm = click.AppContextGroup(name='cm')
81+
82+
@cm.command()
83+
@click.argument("arg1", required=True, help="Arg1 description")
84+
def command_context(arg1):
85+
config = current_injector.get(Config)
86+
print("ALLOWED_HOSTS:", config.ALLOWED_HOSTS, ";ELLAR_CONFIG_MODULE:", config.config_module)
987

10-
from ellar.common import command
1188

12-
@command
13-
def my_new_command():
14-
"""my_new_command cli description """
89+
@cm.command()
90+
@click.argument("arg1", required=True, help="Arg1 description")
91+
def command_wc(arg1):
92+
config = current_injector.get(Config)
93+
print("ALLOWED_HOSTS:", config.ALLOWED_HOSTS, ";ELLAR_CONFIG_MODULE:", config.config_module)
1594
```
95+
All commands registered under `cm` will be executed under within the context of the application.
1696

17-
## **Custom Command with Context**
97+
### **Disabling `with_app_context` in AppContextGroup**
98+
There are some cases where you may want to execute a command under `AppContextGroup` outside application context.
99+
This can be done by setting `with_app_context=False` as command parameter.
18100

19-
Ellar CLI tools is a wrapper round [typer](https://typer.tiangolo.com/).
20-
So, therefore, we can easily get the command context by adding a parameter with the annotation of `typer.Context`
101+
```python
102+
import ellar_cli.click as click
103+
104+
cm = click.AppContextGroup(name='cm')
21105

22-
Ellar CLI adds some meta-data CLI context that provides an interface for interaction with the Ellar project.
106+
@cm.command(with_app_context=False)
107+
@click.argument("arg1", required=True, help="Arg1 description")
108+
def command_wc(arg1):
109+
# config = current_injector.get(Config)
110+
print("ALLOWED_HOSTS:Unavailable;ELLAR_CONFIG_MODULE:Unavailable")
111+
```
23112

24-
For example:
113+
## Async Command
114+
The `ellar_cli.click` package provides a utility decorator function, `run_as_async`,
115+
specifically designed to execute coroutine commands.
116+
This is useful when you want to define asynchronous commands using the `click` package.
117+
Here's an example:
25118

26119
```python
27-
import typing as t
28-
import typer
29-
from ellar.common import command
30-
from ellar_cli.service import EllarCLIService
31-
from ellar_cli.constants import ELLAR_META
32-
33-
@command
34-
def my_new_command(ctx:typer.Context):
35-
"""my_new_command CLI Description """
36-
ellar_cli_service = t.cast(t.Optional[EllarCLIService], ctx.meta.get(ELLAR_META))
37-
app = ellar_cli_service.import_application()
120+
import ellar_cli.click as click
121+
from ellar.core import Config
122+
from ellar.app import current_injector
123+
124+
@click.command()
125+
@click.argument("arg1", required=True, help="Arg1 description")
126+
@click.with_app_context
127+
@click.run_as_async
128+
async def command_context(arg1):
129+
config = current_injector.get(Config)
130+
print("ALLOWED_HOSTS:", config.ALLOWED_HOSTS, ";ELLAR_CONFIG_MODULE:", config.config_module)
38131
```
39-
`EllarCLIService` is an Ellar CLI meta-data for interacting with Ellar project.
40132

41-
Some important method that may be of interest:
133+
In this example, the `run_as_async` decorator enables the `command_context` coroutine
134+
command to be executed appropriately during execution.
135+
136+
## **Custom Command With Ellar**
137+
Let's create a command group `db` which contains sub-commands such as `makemigrations`, `migrate`, `reset-db`, and so on.
138+
139+
To implement this scenario, let's create a file `commands.py` at the root level of the project and add the code below.
140+
```python
141+
from ellar_cli.click import AppContextGroup
142+
143+
db = AppContextGroup(name="db")
42144

43-
- `import_application`: returns application instance.
44-
- `get_application_config`: gets current application config.
45145

46-
## **Register a Custom Command**
146+
@db.command(name="make-migrations")
147+
def makemigrations():
148+
"""Create DB Migration """
47149

48-
Lets, make the `my_new_command` visible on the CLI.
49-
In other for Ellar CLI to identify custom command, its has to be registered to a `@Module` class.
150+
@db.command()
151+
def migrate():
152+
"""Applies Migrations"""
153+
```
154+
155+
### **Registering Command**
50156

51-
For example:
157+
To make the `db` command visible on the CLI, it **must** be registered within a `@Module` class.
158+
This ensures that the Ellar CLI can recognize and identify custom commands.
52159

53160
```python
54-
# project_name/root_module.py
55161
from ellar.common import Module
56162
from ellar.core import ModuleBase
57-
from .commands import my_new_command
163+
from .commands import db
58164

59-
@Module(commands=[my_new_command])
165+
@Module(commands=[db])
60166
class ApplicationModule(ModuleBase):
61167
pass
62168
```
63-
64-
open your terminal and navigate to project directory and run the command below
169+
Open your terminal and navigate to the project directory and run the command below
65170
```shell
66-
ellar --help
171+
ellar db --help
67172
```
68173

69174
command output
70175
```shell
71-
Usage: Ellar, Python Web framework [OPTIONS] COMMAND [ARGS]...
176+
Usage: Ellar, Python Web framework db [OPTIONS] COMMAND [ARGS]...
72177

73178
Options:
74-
-p, --project TEXT Run Specific Command on a specific project
75-
--install-completion [bash|zsh|fish|powershell|pwsh]
76-
Install completion for the specified shell.
77-
--show-completion [bash|zsh|fish|powershell|pwsh]
78-
Show completion for the specified shell, to
79-
copy it or customize the installation.
80-
--help Show this message and exit.
179+
--help Show this message and exit.
81180

82181
Commands:
83-
create-module - Scaffolds Ellar Application Module -
84-
create-project - Scaffolds Ellar Application -
85-
my-new-command - my_new_command cli description
86-
new - Runs a complete Ellar project scaffold and creates...
87-
runserver - Starts Uvicorn Server -
88-
say-hi
89-
```
90-
91-
## **Using Click Commands**
92-
If prefer click commands, Ellar-CLI supports that too. Simply create a click command and register it to any module registered in
93-
the `ApplicationModule`. For example
94-
95-
```python
96-
import click
97-
from ellar.common import JSONResponse, Module, Response, exception_handler
98-
from ellar.core import ModuleBase
99-
from ellar.core.connection import Request
182+
make-migrations Create DB Migration
183+
migrate Applies Migrations
100184

101-
@click.command()
102-
def say_hello():
103-
click.echo("Hello from ellar.")
185+
```
104186

187+
Having explored various methods for crafting commands and understanding the roles of `wrap_app_context` and `run_as_async` decorators,
188+
you now possess the knowledge to create diverse commands for your Ellar application.
105189

106-
@Module(commands=[say_hello])
107-
class ApplicationModule(ModuleBase):
108-
@exception_handler(404)
109-
def exception_404_handler(cls, request: Request, exc: Exception) -> Response:
110-
return JSONResponse({"detail": "Resource not found."})
111-
```
190+
It's crucial to keep in mind that any custom command you develop needs to be registered within a `@Module` class, which,
191+
in turn, should be registered with the `ApplicationModule`.
192+
This ensures that your commands are recognized and integrated into the Ellar application's command-line interface.

docs/stylesheets/extra.css

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -18,5 +18,5 @@ nav.md-nav--primary ul.md-nav__list li.md-nav__item--active.md-nav__item--nested
1818
.md-grid {
1919
margin-left: auto;
2020
margin-right: auto;
21-
max-width: 96%;
21+
max-width: 80%;
2222
}

ellar/core/__init__.py

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -4,14 +4,17 @@
44
from .connection import HTTPConnection, Request, WebSocket
55
from .execution_context import ExecutionContext, HostContext
66
from .guards import GuardConsumer
7+
from .interceptors import EllarInterceptorConsumer
78
from .modules import DynamicModule, LazyModuleImport, ModuleBase, ModuleSetup
89
from .services import Reflector, reflector
10+
from .versioning import VersioningSchemes
911

1012
__all__ = [
1113
"HTTPConnection",
1214
"ExecutionContext",
1315
"HostContext",
1416
"ConfigDefaultTypesMixin",
17+
"EllarInterceptorConsumer",
1518
"ModuleBase",
1619
"Config",
1720
"Request",
@@ -22,6 +25,7 @@
2225
"reflector",
2326
"GuardConsumer",
2427
"LazyModuleImport",
28+
"VersioningSchemes",
2529
]
2630

2731

mkdocs.yml

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -123,7 +123,6 @@ nav:
123123
- create module: cli/create-module-command.md
124124
- runserver: cli/runserver-command.md
125125
- Custom Commands: cli/custom-commands.md
126-
- Command Grouping: cli/command-grouping.md
127126

128127
- WebSockets:
129128
- websockets: websockets/websockets.md

0 commit comments

Comments
 (0)