Skip to content

Commit 3252f04

Browse files
authored
Merge pull request #42 from python-ellar/django-commands-patch
Ellar Version Update
2 parents ae2a120 + c330d53 commit 3252f04

File tree

5 files changed

+157
-43
lines changed

5 files changed

+157
-43
lines changed

ellar_django/commands.py

Lines changed: 38 additions & 38 deletions
Original file line numberDiff line numberDiff line change
@@ -4,12 +4,6 @@
44
import ellar_cli.click as click
55
from django.core.management import get_commands, load_command_class
66

7-
BLACKLISTED_COMMANDS: t.Final[t.Set[str]] = {
8-
"runserver",
9-
"startapp",
10-
"startproject",
11-
}
12-
137

148
class _CommandItem(t.NamedTuple):
159
name: str
@@ -23,18 +17,15 @@ def get_command_description(command_name: str) -> str:
2317
return CommandClass.help
2418

2519

26-
def generate_command_list() -> t.List[_CommandItem]:
20+
def _generate_command_list(
21+
blacklist_commands: t.Set[str],
22+
) -> t.Generator[_CommandItem, None, None]:
2723
commands = get_commands()
28-
command_list: t.List[_CommandItem] = []
2924
for command in commands:
30-
if command in BLACKLISTED_COMMANDS:
25+
if command in blacklist_commands:
3126
continue
3227
description = get_command_description(command)
33-
command_list.append(_CommandItem(name=command, description=description))
34-
return command_list
35-
36-
37-
_django_support_commands = generate_command_list()
28+
yield _CommandItem(name=command, description=description)
3829

3930

4031
def version_callback(ctx: click.Context, _: click.Parameter, value: bool) -> None:
@@ -44,29 +35,14 @@ def version_callback(ctx: click.Context, _: click.Parameter, value: bool) -> Non
4435

4536

4637
def show_help_callback(ctx: click.Context, _: click.Parameter, value: bool) -> None:
47-
command_args = ["manage.py", ctx.info_name, "--help"]
48-
django.core.management.execute_from_command_line(command_args)
49-
50-
51-
@click.group(
52-
name="django",
53-
help="Ellar Django Commands",
54-
)
55-
@click.option(
56-
"-v",
57-
"--version",
58-
callback=version_callback,
59-
help="Show the version and exit.",
60-
is_flag=True,
61-
expose_value=False,
62-
is_eager=True,
63-
)
64-
@click.pass_context
65-
def django_command(ctx: click.Context) -> None:
66-
pass
67-
68-
69-
def _add_django_command(command_item: _CommandItem) -> None:
38+
if value:
39+
command_args = ["manage.py", ctx.info_name, "--help"]
40+
django.core.management.execute_from_command_line(command_args)
41+
42+
43+
def _add_django_command(
44+
django_command: click.Group, command_item: _CommandItem
45+
) -> None:
7046
@django_command.command(
7147
name=command_item.name,
7248
help=command_item.description,
@@ -90,4 +66,28 @@ def command(
9066
django.core.management.execute_from_command_line(command_args)
9167

9268

93-
list(map(_add_django_command, _django_support_commands))
69+
def get_django_command(blacklisted_commands: t.Set[str]) -> click.Group:
70+
@click.group(
71+
name="django",
72+
help="Ellar Django Commands",
73+
)
74+
@click.option(
75+
"-v",
76+
"--version",
77+
callback=version_callback,
78+
help="Show the version and exit.",
79+
is_flag=True,
80+
expose_value=False,
81+
is_eager=True,
82+
)
83+
@click.pass_context
84+
def django_command(ctx: click.Context) -> None:
85+
pass
86+
87+
for command_item in _generate_command_list(blacklisted_commands):
88+
_add_django_command(
89+
django_command=t.cast(click.Group, django_command),
90+
command_item=command_item,
91+
)
92+
93+
return t.cast(click.Group, django_command)

ellar_django/module.py

Lines changed: 23 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,5 @@
11
import os
2+
import typing as t
23

34
from django.core.asgi import get_asgi_application
45
from ellar.common import IModuleSetup, Module, ModuleRouter
@@ -7,21 +8,36 @@
78
from starlette.responses import RedirectResponse
89
from starlette.routing import Mount
910

10-
from .commands import django_command
11+
from ellar_django.commands import get_django_command
12+
1113
from .middleware import DjangoAdminRedirectMiddleware
1214

1315
_router = ModuleRouter()
16+
_default_blacklisted_commands: t.Set[str] = {
17+
"runserver",
18+
"startapp",
19+
"startproject",
20+
}
1421

1522

1623
@_router.get("/")
1724
async def _redirect_route(req: Request) -> RedirectResponse:
1825
return RedirectResponse(url=str(req.base_url))
1926

2027

21-
@Module(commands=[django_command])
28+
@Module()
2229
class DjangoModule(IModuleSetup):
2330
@classmethod
24-
def setup(cls, settings_module: str, path_prefix: str = "/dj") -> "DynamicModule":
31+
def setup(
32+
cls,
33+
settings_module: str,
34+
path_prefix: str = "/dj",
35+
command_blacklist: t.Optional[t.Set[str]] = None,
36+
) -> "DynamicModule":
37+
blacklisted_commands = set(
38+
list(_default_blacklisted_commands) + list(command_blacklist or set())
39+
)
40+
2541
assert path_prefix not in [
2642
"",
2743
"/",
@@ -42,4 +58,7 @@ def setup(cls, settings_module: str, path_prefix: str = "/dj") -> "DynamicModule
4258
),
4359
],
4460
)
45-
return DynamicModule(cls, routers=[mount])
61+
62+
return DynamicModule(
63+
cls, routers=[mount], commands=[get_django_command(blacklisted_commands)]
64+
)

pyproject.toml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -47,7 +47,7 @@ classifiers = [
4747
]
4848

4949
dependencies = [
50-
"ellar-cli >= 0.3.7",
50+
"ellar-cli >= 0.4.1",
5151
"Django >= 3.1",
5252
]
5353

tests/conftest.py

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,4 @@
1+
import click.testing
12
import pytest
23
from django.contrib.auth import get_user_model
34
from ellar.testing import Test
@@ -22,3 +23,8 @@ def admin_user():
2223
def client():
2324
with test_module.get_test_client() as _client:
2425
yield _client
26+
27+
28+
@pytest.fixture
29+
def cli_runner():
30+
return click.testing.CliRunner()

tests/test_django_commands.py

Lines changed: 89 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,21 @@
11
import pytest
22
from django.core.management import call_command
33
from django.core.management.base import CommandError
4+
from ellar.common.constants import MODULE_METADATA
5+
from ellar.reflect import reflect
6+
7+
from ellar_django.module import DjangoModule, _default_blacklisted_commands
8+
9+
HELP_OUTPUT = """Usage: django [OPTIONS] COMMAND [ARGS]...
10+
11+
Ellar Django Commands
12+
13+
Options:
14+
-v, --version Show the version and exit.
15+
--help Show this message and exit.
16+
17+
Commands:
18+
"""
419

520

621
def test_command_succeeds() -> None:
@@ -10,3 +25,77 @@ def test_command_succeeds() -> None:
1025
def test_nonexistent_command_fails() -> None:
1126
with pytest.raises(CommandError, match="Unknown command"):
1227
call_command("nonexistent_command")
28+
29+
30+
def test_command_help(cli_runner):
31+
with reflect.context():
32+
DjangoModule.setup(
33+
settings_module="example_app.wsgi_django.settings"
34+
).apply_configuration()
35+
django_command_group = reflect.get_metadata(
36+
MODULE_METADATA.COMMANDS, DjangoModule
37+
)[0]
38+
39+
res = cli_runner.invoke(django_command_group, ["--help"])
40+
assert res.exit_code == 0
41+
assert HELP_OUTPUT in res.stdout
42+
43+
44+
def test_check_help(cli_runner):
45+
with reflect.context():
46+
DjangoModule.setup(
47+
settings_module="example_app.wsgi_django.settings"
48+
).apply_configuration()
49+
django_command_group = reflect.get_metadata(
50+
MODULE_METADATA.COMMANDS, DjangoModule
51+
)[0]
52+
53+
res = cli_runner.invoke(django_command_group, ["check", "--help"])
54+
assert res.exit_code == 0
55+
assert (
56+
"usage: manage.py check [-h] [--tag TAGS] [--list-tags] [--deploy]"
57+
in res.stdout
58+
)
59+
60+
61+
@pytest.mark.parametrize("command_name", list(_default_blacklisted_commands))
62+
def test_default_blacklist_command(cli_runner, command_name):
63+
with reflect.context():
64+
DjangoModule.setup(
65+
settings_module="example_app.wsgi_django.settings"
66+
).apply_configuration()
67+
django_command_group = reflect.get_metadata(
68+
MODULE_METADATA.COMMANDS, DjangoModule
69+
)[0]
70+
71+
res = cli_runner.invoke(django_command_group, [command_name])
72+
assert res.exit_code == 2
73+
assert f"Error: No such command '{command_name}'" in res.stdout
74+
75+
76+
def test_django_version(cli_runner):
77+
with reflect.context():
78+
DjangoModule.setup(
79+
settings_module="example_app.wsgi_django.settings"
80+
).apply_configuration()
81+
django_command_group = reflect.get_metadata(
82+
MODULE_METADATA.COMMANDS, DjangoModule
83+
)[0]
84+
85+
res = cli_runner.invoke(django_command_group, ["-v"])
86+
assert res.exit_code == 0
87+
assert "Django Version: " in res.stdout
88+
89+
90+
def test_collect_static_version(cli_runner):
91+
with reflect.context():
92+
DjangoModule.setup(
93+
settings_module="example_app.wsgi_django.settings"
94+
).apply_configuration()
95+
django_command_group = reflect.get_metadata(
96+
MODULE_METADATA.COMMANDS, DjangoModule
97+
)[0]
98+
99+
res = cli_runner.invoke(django_command_group, ["check", "db_models"])
100+
assert res.exit_code == 0
101+
assert res.stdout == "System check identified no issues (0 silenced).\n"

0 commit comments

Comments
 (0)