Skip to content

Commit 4108c13

Browse files
thijskramerNiicck
andauthored
Setup tests, linting and codestyle checks (MrBin99#77)
* setup tests, linting and codestyle checks * add a simple smoke test with some standard django settings * add Github Action to run Tox test matrix * fix codestyle warning * update pre-commit config * re-format code with black * add troves to clarify for which django and python versions Django-Vite is intended * fix linting errors * add tests for templatetags * Add tests to achieve 100% Test Coverage (MrBin99#91) * create patch_settings fixture * 100% test coverage * add Python 3.12 to test matrix * do not fail tests under 100%; also ensure imports are sorted --------- Co-authored-by: Nick Ivons <[email protected]>
1 parent 57156f4 commit 4108c13

24 files changed

+985
-73
lines changed

.github/workflows/pre-commit.yml

+1-1
Original file line numberDiff line numberDiff line change
@@ -12,4 +12,4 @@ jobs:
1212
- uses: "actions/setup-python@v4"
1313
with:
1414
python-version: 3.x
15-
- uses: "pre-commit/action@v2.0.3"
15+
- uses: "pre-commit/action@v3.0.0"

.github/workflows/test.yml

+31
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,31 @@
1+
name: Run Test Matrix
2+
3+
on:
4+
push:
5+
branches: [master]
6+
pull_request:
7+
branches: [master]
8+
9+
concurrency:
10+
group: ${{ github.head_ref }}
11+
cancel-in-progress: true
12+
13+
jobs:
14+
build:
15+
runs-on: ubuntu-latest
16+
strategy:
17+
matrix:
18+
python-version: ["3.8", "3.9", "3.10", "3.11", "3.12"]
19+
20+
steps:
21+
- uses: actions/checkout@v3
22+
- name: Set up Python ${{ matrix.python-version }}
23+
uses: actions/setup-python@v4
24+
with:
25+
python-version: ${{ matrix.python-version }}
26+
- name: Install dependencies
27+
run: |
28+
python -m pip install --upgrade pip
29+
python -m pip install tox tox-gh-actions
30+
- name: Test with tox
31+
run: tox

.gitignore

+4
Original file line numberDiff line numberDiff line change
@@ -1 +1,5 @@
11
__pycache__/
2+
.tox
3+
.vscode
4+
.coverage
5+
*.egg-info

.pre-commit-config.yaml

+2-7
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
repos:
22
- repo: "https://github.com/pre-commit/pre-commit-hooks"
3-
rev: "v4.1.0"
3+
rev: "v4.4.0"
44
hooks:
55
- id: "check-toml"
66
- id: "check-yaml"
@@ -10,15 +10,10 @@ repos:
1010
- id: "trailing-whitespace"
1111

1212
- repo: "https://github.com/psf/black"
13-
rev: "22.3.0"
13+
rev: "23.3.0"
1414
hooks:
1515
- id: "black"
1616

17-
- repo: "https://github.com/PyCQA/flake8"
18-
rev: "4.0.1"
19-
hooks:
20-
- id: "flake8"
21-
2217
- repo: "https://github.com/commitizen-tools/commitizen"
2318
rev: master
2419
hooks:

django_vite/apps.py

+7-6
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,9 @@
1+
from contextlib import suppress
2+
13
from django.apps import AppConfig
24
from django.core.checks import Warning, register
35

6+
from .exceptions import DjangoViteManifestError
47
from .templatetags.django_vite import DjangoViteAssetLoader
58

69

@@ -9,12 +12,10 @@ class DjangoViteAppConfig(AppConfig):
912
verbose_name = "Django Vite"
1013

1114
def ready(self) -> None:
12-
try:
13-
# Create Loader instance at startup to prevent threading problems.
15+
with suppress(DjangoViteManifestError):
16+
# Create Loader instance at startup to prevent threading problems,
17+
# but do not crash while doing so.
1418
DjangoViteAssetLoader.instance()
15-
except RuntimeError:
16-
# Just continue, the system check below outputs a warning.
17-
pass
1819

1920

2021
@register
@@ -25,7 +26,7 @@ def check_loader_instance(**kwargs):
2526
# Make Loader instance at startup to prevent threading problems
2627
DjangoViteAssetLoader.instance()
2728
return []
28-
except RuntimeError as exception:
29+
except DjangoViteManifestError as exception:
2930
return [
3031
Warning(
3132
exception,

django_vite/exceptions.py

+10
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,10 @@
1+
class DjangoViteManifestError(RuntimeError):
2+
"""Manifest parsing failed."""
3+
4+
pass
5+
6+
7+
class DjangoViteAssetNotFoundError(RuntimeError):
8+
"""Vite Asset could not be found."""
9+
10+
pass

django_vite/templatetags/django_vite.py

+32-54
Original file line numberDiff line numberDiff line change
@@ -1,15 +1,16 @@
11
import json
22
from pathlib import Path
3-
from typing import Dict, List, Callable
3+
from typing import Callable, Dict, List
44
from urllib.parse import urljoin
55

66
from django import template
77
from django.apps import apps
88
from django.conf import settings
99
from django.utils.safestring import mark_safe
1010

11-
register = template.Library()
11+
from django_vite.exceptions import DjangoViteAssetNotFoundError, DjangoViteManifestError
1212

13+
register = template.Library()
1314

1415
# If using in development or production mode.
1516
DJANGO_VITE_DEV_MODE = getattr(settings, "DJANGO_VITE_DEV_MODE", False)
@@ -25,9 +26,7 @@
2526
)
2627

2728
# Default Vite server port.
28-
DJANGO_VITE_DEV_SERVER_PORT = getattr(
29-
settings, "DJANGO_VITE_DEV_SERVER_PORT", 3000
30-
)
29+
DJANGO_VITE_DEV_SERVER_PORT = getattr(settings, "DJANGO_VITE_DEV_SERVER_PORT", 3000)
3130

3231
# Default Vite server path to HMR script.
3332
DJANGO_VITE_WS_CLIENT_URL = getattr(
@@ -42,12 +41,10 @@
4241
# Must be included in your "STATICFILES_DIRS".
4342
# In Django production mode this folder need to be collected as static
4443
# files using "python manage.py collectstatic".
45-
DJANGO_VITE_ASSETS_PATH = Path(getattr(settings, "DJANGO_VITE_ASSETS_PATH"))
44+
DJANGO_VITE_ASSETS_PATH = Path(settings.DJANGO_VITE_ASSETS_PATH)
4645

4746
# Prefix for STATIC_URL
48-
DJANGO_VITE_STATIC_URL_PREFIX = getattr(
49-
settings, "DJANGO_VITE_STATIC_URL_PREFIX", ""
50-
)
47+
DJANGO_VITE_STATIC_URL_PREFIX = getattr(settings, "DJANGO_VITE_STATIC_URL_PREFIX", "")
5148

5249
DJANGO_VITE_STATIC_ROOT = (
5350
DJANGO_VITE_ASSETS_PATH
@@ -68,9 +65,7 @@
6865
settings, "DJANGO_VITE_LEGACY_POLYFILLS_MOTIF", "legacy-polyfills"
6966
)
7067

71-
DJANGO_VITE_STATIC_URL = urljoin(
72-
settings.STATIC_URL, DJANGO_VITE_STATIC_URL_PREFIX
73-
)
68+
DJANGO_VITE_STATIC_URL = urljoin(settings.STATIC_URL, DJANGO_VITE_STATIC_URL_PREFIX)
7469

7570
# Make sure 'DJANGO_VITE_STATIC_URL' finish with a '/'
7671
if DJANGO_VITE_STATIC_URL[-1] != "/":
@@ -110,7 +105,7 @@ def generate_vite_asset(
110105
script tags.
111106
112107
Raises:
113-
RuntimeError: If cannot find the file path in the
108+
DjangoViteAssetNotFoundError: If cannot find the file path in the
114109
manifest (only in production).
115110
116111
Returns:
@@ -125,7 +120,7 @@ def generate_vite_asset(
125120
)
126121

127122
if not self._manifest or path not in self._manifest:
128-
raise RuntimeError(
123+
raise DjangoViteAssetNotFoundError(
129124
f"Cannot find {path} in Vite manifest "
130125
f"at {DJANGO_VITE_MANIFEST_PATH}"
131126
)
@@ -158,9 +153,7 @@ def generate_vite_asset(
158153
for dep in manifest_entry.get("imports", []):
159154
dep_manifest_entry = self._manifest[dep]
160155
dep_file = dep_manifest_entry["file"]
161-
url = DjangoViteAssetLoader._generate_production_server_url(
162-
dep_file
163-
)
156+
url = DjangoViteAssetLoader._generate_production_server_url(dep_file)
164157
tags.append(
165158
DjangoViteAssetLoader._generate_preload_tag(
166159
url,
@@ -188,7 +181,7 @@ def preload_vite_asset(
188181
str -- All tags to preload this file in your HTML page.
189182
190183
Raises:
191-
RuntimeError: If cannot find the file path in the
184+
DjangoViteAssetNotFoundError: if cannot find the file path in the
192185
manifest.
193186
194187
Returns:
@@ -199,7 +192,7 @@ def preload_vite_asset(
199192
return ""
200193

201194
if not self._manifest or path not in self._manifest:
202-
raise RuntimeError(
195+
raise DjangoViteAssetNotFoundError(
203196
f"Cannot find {path} in Vite manifest "
204197
f"at {DJANGO_VITE_MANIFEST_PATH}"
205198
)
@@ -216,9 +209,7 @@ def preload_vite_asset(
216209
}
217210

218211
manifest_file = manifest_entry["file"]
219-
url = DjangoViteAssetLoader._generate_production_server_url(
220-
manifest_file
221-
)
212+
url = DjangoViteAssetLoader._generate_production_server_url(manifest_file)
222213
tags.append(
223214
DjangoViteAssetLoader._generate_preload_tag(
224215
url,
@@ -233,9 +224,7 @@ def preload_vite_asset(
233224
for dep in manifest_entry.get("imports", []):
234225
dep_manifest_entry = self._manifest[dep]
235226
dep_file = dep_manifest_entry["file"]
236-
url = DjangoViteAssetLoader._generate_production_server_url(
237-
dep_file
238-
)
227+
url = DjangoViteAssetLoader._generate_production_server_url(dep_file)
239228
tags.append(
240229
DjangoViteAssetLoader._generate_preload_tag(
241230
url,
@@ -291,10 +280,8 @@ def _generate_css_files_of_asset(
291280
if "css" in manifest_entry:
292281
for css_path in manifest_entry["css"]:
293282
if css_path not in already_processed:
294-
url = (
295-
DjangoViteAssetLoader._generate_production_server_url(
296-
css_path
297-
)
283+
url = DjangoViteAssetLoader._generate_production_server_url(
284+
css_path
298285
)
299286
tags.append(tag_generator(url))
300287

@@ -311,7 +298,7 @@ def generate_vite_asset_url(self, path: str) -> str:
311298
path {str} -- Path to a Vite asset.
312299
313300
Raises:
314-
RuntimeError: If cannot find the asset path in the
301+
DjangoViteAssetNotFoundError: If cannot find the asset path in the
315302
manifest (only in production).
316303
317304
Returns:
@@ -322,7 +309,7 @@ def generate_vite_asset_url(self, path: str) -> str:
322309
return DjangoViteAssetLoader._generate_vite_server_url(path)
323310

324311
if not self._manifest or path not in self._manifest:
325-
raise RuntimeError(
312+
raise DjangoViteAssetNotFoundError(
326313
f"Cannot find {path} in Vite manifest "
327314
f"at {DJANGO_VITE_MANIFEST_PATH}"
328315
)
@@ -346,7 +333,7 @@ def generate_vite_legacy_polyfills(
346333
script tags.
347334
348335
Raises:
349-
RuntimeError: If polyfills path not found inside
336+
DjangoViteAssetNotFoundError: If polyfills path not found inside
350337
the 'manifest.json' (only in production).
351338
352339
Returns:
@@ -367,7 +354,7 @@ def generate_vite_legacy_polyfills(
367354
attrs=scripts_attrs,
368355
)
369356

370-
raise RuntimeError(
357+
raise DjangoViteAssetNotFoundError(
371358
f"Vite legacy polyfills not found in manifest "
372359
f"at {DJANGO_VITE_MANIFEST_PATH}"
373360
)
@@ -391,7 +378,7 @@ def generate_vite_legacy_asset(
391378
script tags.
392379
393380
Raises:
394-
RuntimeError: If cannot find the asset path in the
381+
DjangoViteAssetNotFoundError: If cannot find the asset path in the
395382
manifest (only in production).
396383
397384
Returns:
@@ -402,7 +389,7 @@ def generate_vite_legacy_asset(
402389
return ""
403390

404391
if not self._manifest or path not in self._manifest:
405-
raise RuntimeError(
392+
raise DjangoViteAssetNotFoundError(
406393
f"Cannot find {path} in Vite manifest "
407394
f"at {DJANGO_VITE_MANIFEST_PATH}"
408395
)
@@ -422,19 +409,19 @@ def _parse_manifest(self) -> None:
422409
Read and parse the Vite manifest file.
423410
424411
Raises:
425-
RuntimeError: if cannot load the file or JSON in file is malformed.
412+
DjangoViteManifestError: if cannot load the file or JSON in file is
413+
malformed.
426414
"""
427415

428416
try:
429-
manifest_file = open(DJANGO_VITE_MANIFEST_PATH, "r")
430-
manifest_content = manifest_file.read()
431-
manifest_file.close()
417+
with open(DJANGO_VITE_MANIFEST_PATH, "r") as manifest_file:
418+
manifest_content = manifest_file.read()
432419
self._manifest = json.loads(manifest_content)
433420
except Exception as error:
434-
raise RuntimeError(
421+
raise DjangoViteManifestError(
435422
f"Cannot read Vite manifest file at "
436423
f"{DJANGO_VITE_MANIFEST_PATH} : {str(error)}"
437-
)
424+
) from error
438425

439426
@classmethod
440427
def instance(cls):
@@ -496,9 +483,7 @@ def _generate_script_tag(src: str, attrs: Dict[str, str]) -> str:
496483
str -- The script tag.
497484
"""
498485

499-
attrs_str = " ".join(
500-
[f'{key}="{value}"' for key, value in attrs.items()]
501-
)
486+
attrs_str = " ".join([f'{key}="{value}"' for key, value in attrs.items()])
502487

503488
return f'<script {attrs_str} src="{src}"></script>'
504489

@@ -532,9 +517,7 @@ def _generate_stylesheet_preload_tag(href: str) -> str:
532517

533518
@staticmethod
534519
def _generate_preload_tag(href: str, attrs: Dict[str, str]) -> str:
535-
attrs_str = " ".join(
536-
[f'{key}="{value}"' for key, value in attrs.items()]
537-
)
520+
attrs_str = " ".join([f'{key}="{value}"' for key, value in attrs.items()])
538521

539522
return f'<link href="{href}" {attrs_str} />'
540523

@@ -682,7 +665,6 @@ def vite_preload_asset(
682665
manifest (only in production).
683666
684667
"""
685-
686668
assert path is not None
687669

688670
return DjangoViteAssetLoader.instance().preload_vite_asset(path)
@@ -731,9 +713,7 @@ def vite_legacy_polyfills(**kwargs: Dict[str, str]) -> str:
731713
str -- The script tag to the polyfills.
732714
"""
733715

734-
return DjangoViteAssetLoader.instance().generate_vite_legacy_polyfills(
735-
**kwargs
736-
)
716+
return DjangoViteAssetLoader.instance().generate_vite_legacy_polyfills(**kwargs)
737717

738718

739719
@register.simple_tag
@@ -765,9 +745,7 @@ def vite_legacy_asset(
765745

766746
assert path is not None
767747

768-
return DjangoViteAssetLoader.instance().generate_vite_legacy_asset(
769-
path, **kwargs
770-
)
748+
return DjangoViteAssetLoader.instance().generate_vite_legacy_asset(path, **kwargs)
771749

772750

773751
@register.simple_tag

0 commit comments

Comments
 (0)