Skip to content

Commit c57b9fb

Browse files
authored
Merge pull request #20 from python-ellar/proj_info_update
Project PyPI info updates
2 parents 0c8e07c + 3aa280e commit c57b9fb

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

44 files changed

+710
-85
lines changed

.github/workflows/publish.yml

Lines changed: 8 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -17,9 +17,12 @@ jobs:
1717
- name: Install Flit
1818
run: pip install flit
1919
- name: Install Dependencies
20-
run: flit install --symlink
20+
run: make install
21+
- name: Install build dependencies
22+
run: pip install build
23+
- name: Build distribution
24+
run: python -m build
2125
- name: Publish
22-
env:
23-
FLIT_USERNAME: ${{ secrets.FLIT_USERNAME }}
24-
FLIT_PASSWORD: ${{ secrets.FLIT_PASSWORD }}
25-
run: flit publish
26+
uses: pypa/[email protected]
27+
with:
28+
password: ${{ secrets.PYPI_API_TOKEN }}

.github/workflows/test.yml

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -14,11 +14,11 @@ jobs:
1414
- name: Set up Python
1515
uses: actions/setup-python@v5
1616
with:
17-
python-version: 3.8
17+
python-version: "3.10"
1818
- name: Install Flit
1919
run: pip install flit
2020
- name: Install Dependencies
21-
run: flit install --symlink
21+
run: make install
2222
- name: Test
2323
run: make test-cov
2424
- name: Coverage

.github/workflows/test_full.yml

Lines changed: 9 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -10,8 +10,13 @@ jobs:
1010
runs-on: ubuntu-latest
1111
strategy:
1212
matrix:
13-
python-version: ['3.8', '3.9', '3.10', '3.11']
14-
django-version: ['<3.0', '<3.1', '<3.2', '<3.3', '<4.1', '<4.2']
13+
python-version: ['3.8', '3.9', '3.10', '3.11', '3.12']
14+
django-version: ['<3.2', '<3.3', '<4.2', '<4.3']
15+
exclude:
16+
- python-version: '3.12'
17+
django-version: '<3.2'
18+
- python-version: '3.12'
19+
django-version: '<3.3'
1520

1621
steps:
1722
- uses: actions/checkout@v4
@@ -24,7 +29,7 @@ jobs:
2429
- name: Install Flit
2530
run: pip install flit
2631
- name: Install Dependencies
27-
run: flit install --symlink
32+
run: make install
2833
- name: Test
2934
run: pytest tests
3035

@@ -39,9 +44,7 @@ jobs:
3944
- name: Install Flit
4045
run: pip install flit
4146
- name: Install Dependencies
42-
run: flit install --symlink
43-
- name: Black
44-
run: black --check ellar_django tests
47+
run: make install
4548
- name: Ruff Linting Check
4649
run: ruff check ellar_django tests
4750
- name: mypy

Makefile

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -11,19 +11,19 @@ clean: ## Removing cached python compiled files
1111
find . -name __pycache__ | xargs rm -rfv
1212

1313
install: ## Install dependencies
14-
flit install --deps develop --symlink
14+
pip install -r requirements.txt
15+
flit install --symlink
1516

1617
install-full: ## Install dependencies
1718
make install
1819
pre-commit install -f
1920

2021
lint: ## Run code linters
21-
black --check ellar_django tests
2222
ruff check ellar_django tests
2323
mypy ellar_django
2424

2525
fmt format: ## Run code formatters
26-
black ellar_django tests
26+
ruff format ellar_django tests
2727
ruff check --fix ellar_django tests
2828

2929
test: ## Run tests

README.md

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -205,6 +205,7 @@ MIDDLEWARE = [
205205
"whitenoise.middleware.WhiteNoiseMiddleware",
206206
...
207207
]
208+
WHITENOISE_STATIC_PREFIX = '/static/'
208209
...
209210
```
210211
So when you visit [http://localhost:8000/-django-example/admin](http://localhost:8000/-django-example/admin) again, the page should be functional.

ellar_django/commands.py

Lines changed: 146 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -1,26 +1,156 @@
11
import sys
2+
import typing as t
23

3-
from ellar.common.commands import command
4+
import django
5+
import ellar_cli.click as click
46

5-
HELP_MESSAGE = """
6-
Ellar will always intercept and command with '--help'.
77

8-
So if you want to get help on any django command,
9-
simply wrap the command and --help in quotes
8+
class _CommandItem(t.NamedTuple):
9+
name: str
10+
description: str
1011

11-
For example: ellar django 'migrate --help'
12-
"""
1312

13+
_django_support_commands: t.List[_CommandItem] = [
14+
_CommandItem(
15+
name="migrate",
16+
description="Synchronizes the database state with the current set of models and migrations",
17+
),
18+
_CommandItem(
19+
name="makemigrations",
20+
description="Creates new migrations based on the changes detected to your models.",
21+
),
22+
_CommandItem(
23+
name="check",
24+
description="inspects the entire Django project for common problems",
25+
),
26+
_CommandItem(
27+
name="createcachetable",
28+
description="Creates the cache tables for use with the database cache backend",
29+
),
30+
_CommandItem(
31+
name="dbshell",
32+
description="Runs the command-line client for the database engine",
33+
),
34+
_CommandItem(
35+
name="diffsettings",
36+
description="Displays differences between the current settings and default settings",
37+
),
38+
_CommandItem(
39+
name="dumpdata",
40+
description="Outputs to standard output all data in the database",
41+
),
42+
_CommandItem(
43+
name="flush",
44+
description="Removes all data from the database and re-executes any post-synchronization handlers",
45+
),
46+
_CommandItem(
47+
name="inspectdb", description="Introspects the database tables in the database"
48+
),
49+
_CommandItem(
50+
name="loaddata",
51+
description="Searches for and loads the contents into the database.",
52+
),
53+
_CommandItem(
54+
name="optimizemigration",
55+
description="Optimizes the operations for the named migration and overrides the existing file",
56+
),
57+
_CommandItem(
58+
name="showmigrations", description="Shows all migrations in a project."
59+
),
60+
_CommandItem(
61+
name="sqlflush",
62+
description="Prints the SQL statements that would be executed for the flush command",
63+
),
64+
_CommandItem(
65+
name="sqlmigrate", description="Prints the SQL for the named migration."
66+
),
67+
_CommandItem(
68+
name="sqlsequencereset",
69+
description="Prints the SQL statements for resetting sequences for the given app name(s)",
70+
),
71+
_CommandItem(
72+
name="squashmigrations", description="Squashes the migrations for app_label"
73+
),
74+
_CommandItem(
75+
name="startapp", description="Creates a Django app directory structure"
76+
),
77+
_CommandItem(
78+
name="changepassword",
79+
description="This command is only available if Django’s authentication system",
80+
),
81+
_CommandItem(
82+
name="createsuperuser",
83+
description="Creates a superuser account (a user who has all permissions)",
84+
),
85+
_CommandItem(
86+
name="collectstatic", description="Expose static files to STATIC_ROOT folder"
87+
),
88+
_CommandItem(name="findstatic", description="Search for a static file location"),
89+
_CommandItem(
90+
name="clearsessions",
91+
description="Can be run as a cron job or directly to clean out expired sessions.",
92+
),
93+
]
1494

15-
@command(
95+
96+
def version_callback(ctx: click.Context, _: t.Any, value: bool) -> None:
97+
if value:
98+
click.echo(f"Django Version: {django.__version__}")
99+
raise click.Exit(0)
100+
101+
102+
@click.group(
16103
name="django",
17-
context_settings={"ignore_unknown_options": True, "allow_extra_args": True},
18-
help=HELP_MESSAGE,
104+
help="- Ellar Django Commands",
19105
)
20-
def django_command_wrapper() -> None:
21-
from django.core.management import execute_from_command_line
106+
@click.option(
107+
"-v",
108+
"--version",
109+
callback=version_callback,
110+
help="Show the version and exit.",
111+
is_flag=True,
112+
expose_value=False,
113+
is_eager=True,
114+
)
115+
@click.pass_context
116+
def django_command(ctx: click.Context) -> None:
117+
pass
118+
119+
120+
def _add_django_command(command_item: _CommandItem) -> None:
121+
def help_callback(ctx: click.Context, _: t.Any, value: bool) -> None:
122+
from django.core.management import execute_from_command_line
123+
124+
if value:
125+
args = ["manage.py", command_item.name, "--help"]
126+
127+
execute_from_command_line(args)
128+
raise click.Exit(0)
129+
130+
@django_command.command(
131+
name=command_item.name,
132+
help=command_item.description,
133+
context_settings={"ignore_unknown_options": True, "allow_extra_args": True},
134+
add_help_option=False,
135+
)
136+
@click.option(
137+
"-h",
138+
"--help",
139+
callback=help_callback,
140+
help="Show the version and exit.",
141+
is_flag=True,
142+
expose_value=False,
143+
is_eager=True,
144+
)
145+
@click.with_app_context
146+
def _command() -> None:
147+
from django.core.management import execute_from_command_line
148+
149+
args = ["manage.py", command_item.name]
150+
151+
for item in sys.argv[3:]:
152+
args.extend(item.split(" "))
153+
execute_from_command_line(args)
154+
22155

23-
args = []
24-
for item in sys.argv[1:]:
25-
args.extend(item.split(" "))
26-
execute_from_command_line(args)
156+
list(map(_add_django_command, _django_support_commands))

ellar_django/middleware.py

Lines changed: 34 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,34 @@
1+
import typing as t
2+
3+
from ellar.common.types import ASGIApp, TMessage, TReceive, TScope, TSend
4+
from starlette.datastructures import MutableHeaders
5+
6+
7+
class DjangoAdminRedirectMiddleware:
8+
def __init__(self, app: ASGIApp, path_prefix: str) -> None:
9+
self.app = app
10+
self.path_prefix = path_prefix
11+
12+
async def __call__(self, scope: TScope, receive: TReceive, send: TSend) -> t.Any:
13+
if scope["type"] != "http": # pragma: no cover
14+
return await self.app(scope, receive, send)
15+
16+
async def sender(message: TMessage) -> None:
17+
if message["type"] == "http.response.start":
18+
headers = message.get("headers")
19+
if headers:
20+
working_headers = MutableHeaders(
21+
raw=[
22+
(key.decode("latin-1").lower().encode("latin-1"), value)
23+
for key, value in headers
24+
]
25+
)
26+
location = working_headers.get("location")
27+
if location and not location.startswith(self.path_prefix):
28+
working_headers["Location"] = (
29+
f"{self.path_prefix}{working_headers['Location']}"
30+
)
31+
message["headers"] = working_headers.raw
32+
await send(message)
33+
34+
return await self.app(scope, receive, sender)

ellar_django/module.py

Lines changed: 21 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -2,12 +2,13 @@
22

33
from django.core.asgi import get_asgi_application
44
from ellar.common import IModuleSetup, Module, ModuleRouter
5-
from ellar.common.routing import Mount
65
from ellar.core import DynamicModule, Request
7-
from ellar.core.routing import ModuleRouterFactory
6+
from ellar.core.router_builders import ModuleRouterBuilder
87
from starlette.responses import RedirectResponse
8+
from starlette.routing import Mount
99

10-
from .commands import django_command_wrapper
10+
from .commands import django_command
11+
from .middleware import DjangoAdminRedirectMiddleware
1112

1213
_router = ModuleRouter()
1314

@@ -17,14 +18,28 @@ async def _redirect_route(req: Request) -> RedirectResponse:
1718
return RedirectResponse(url=str(req.base_url))
1819

1920

20-
@Module(commands=[django_command_wrapper])
21+
@Module(commands=[django_command])
2122
class DjangoModule(IModuleSetup):
2223
@classmethod
2324
def setup(cls, settings_module: str, path_prefix: str = "/dj") -> "DynamicModule":
25+
assert path_prefix not in [
26+
"",
27+
"/",
28+
], "Invalid path prefix, please set a valid path prefix"
29+
2430
os.environ.setdefault("DJANGO_SETTINGS_MODULE", settings_module)
25-
ModuleRouterFactory.build(_router)
31+
_router_as_mount = ModuleRouterBuilder.build(_router)
2632

2733
mount = Mount(
28-
path_prefix, routes=[_router, Mount("/", app=get_asgi_application())]
34+
path_prefix,
35+
routes=[
36+
_router_as_mount,
37+
Mount(
38+
"/",
39+
app=DjangoAdminRedirectMiddleware(
40+
get_asgi_application(), path_prefix
41+
),
42+
),
43+
],
2944
)
3045
return DynamicModule(cls, routers=[mount])

0 commit comments

Comments
 (0)