Skip to content

Commit 055dde4

Browse files
committed
upgraded ellar version, added some test and moved django commands to be generated dynamically
1 parent 9786a73 commit 055dde4

File tree

5 files changed

+185
-43
lines changed

5 files changed

+185
-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: 117 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,49 @@
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+
changepassword [django] Change a user's password for...
19+
check [django] Checks the entire Django project for...
20+
clearsessions [django] Can be run as a cronjob or directly...
21+
collectstatic [django] Collect static files in a single...
22+
compilemessages [django] Compiles .po files to .mo files for...
23+
createcachetable [django] Creates the tables needed to use the...
24+
createsuperuser [django] Used to create a superuser.
25+
dbshell [django] Runs the command-line client for...
26+
diffsettings [django] Displays differences between the...
27+
dumpdata [django] Output the contents of the database...
28+
findstatic [django] Finds the absolute paths for the...
29+
flush [django] Removes ALL DATA from the database,...
30+
inspectdb [django] Introspects the database tables in...
31+
loaddata [django] Installs the named fixture(s) in the...
32+
makemessages [django] Runs over the entire source tree of...
33+
makemigrations [django] Creates new migration(s) for apps.
34+
migrate [django] Updates database schema.
35+
optimizemigration [django] Optimizes the operations for the...
36+
remove_stale_contenttypes [django]
37+
sendtestemail [django] Sends a test email to the email...
38+
shell [django] Runs a Python interactive interpreter.
39+
showmigrations [django] Shows all available migrations for...
40+
sqlflush [django] Returns a list of the SQL statements...
41+
sqlmigrate [django] Prints the SQL statements for the...
42+
sqlsequencereset [django] Prints the SQL statements for...
43+
squashmigrations [django] Squashes an existing set of...
44+
test [django] Discover and run tests in the...
45+
testserver [django] Runs a development server with data...
46+
"""
447

548

649
def test_command_succeeds() -> None:
@@ -10,3 +53,77 @@ def test_command_succeeds() -> None:
1053
def test_nonexistent_command_fails() -> None:
1154
with pytest.raises(CommandError, match="Unknown command"):
1255
call_command("nonexistent_command")
56+
57+
58+
def test_command_help(cli_runner):
59+
with reflect.context():
60+
DjangoModule.setup(
61+
settings_module="example_app.wsgi_django.settings"
62+
).apply_configuration()
63+
django_command_group = reflect.get_metadata(
64+
MODULE_METADATA.COMMANDS, DjangoModule
65+
)[0]
66+
67+
res = cli_runner.invoke(django_command_group, ["--help"])
68+
assert res.exit_code == 0
69+
assert res.stdout == HELP_OUTPUT
70+
71+
72+
def test_check_help(cli_runner):
73+
with reflect.context():
74+
DjangoModule.setup(
75+
settings_module="example_app.wsgi_django.settings"
76+
).apply_configuration()
77+
django_command_group = reflect.get_metadata(
78+
MODULE_METADATA.COMMANDS, DjangoModule
79+
)[0]
80+
81+
res = cli_runner.invoke(django_command_group, ["check", "--help"])
82+
assert res.exit_code == 0
83+
assert (
84+
"usage: manage.py check [-h] [--tag TAGS] [--list-tags] [--deploy]"
85+
in res.stdout
86+
)
87+
88+
89+
@pytest.mark.parametrize("command_name", list(_default_blacklisted_commands))
90+
def test_default_blacklist_command(cli_runner, command_name):
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, [command_name])
100+
assert res.exit_code == 2
101+
assert f"Error: No such command '{command_name}'" in res.stdout
102+
103+
104+
def test_django_version(cli_runner):
105+
with reflect.context():
106+
DjangoModule.setup(
107+
settings_module="example_app.wsgi_django.settings"
108+
).apply_configuration()
109+
django_command_group = reflect.get_metadata(
110+
MODULE_METADATA.COMMANDS, DjangoModule
111+
)[0]
112+
113+
res = cli_runner.invoke(django_command_group, ["-v"])
114+
assert res.exit_code == 0
115+
assert "Django Version: " in res.stdout
116+
117+
118+
def test_collect_static_version(cli_runner):
119+
with reflect.context():
120+
DjangoModule.setup(
121+
settings_module="example_app.wsgi_django.settings"
122+
).apply_configuration()
123+
django_command_group = reflect.get_metadata(
124+
MODULE_METADATA.COMMANDS, DjangoModule
125+
)[0]
126+
127+
res = cli_runner.invoke(django_command_group, ["check", "db_models"])
128+
assert res.exit_code == 0
129+
assert res.stdout == "System check identified no issues (0 silenced).\n"

0 commit comments

Comments
 (0)