Skip to content

Commit 237ea68

Browse files
committed
implements #156
1 parent cc188da commit 237ea68

File tree

13 files changed

+232
-6
lines changed

13 files changed

+232
-6
lines changed

LICENSE

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
MIT License
22

3-
Copyright (c) 2020-2023 Brian Kohan
3+
Copyright (c) 2020-2024 Brian Kohan
44

55
Permission is hereby granted, free of charge, to any person obtaining a copy
66
of this software and associated documentation files (the "Software"), to deal

doc/source/changelog.rst

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,11 @@
22
Change Log
33
==========
44

5+
v3.1.0 (16-JUL-2024)
6+
====================
7+
8+
* Implemented `Support a no-render copy operation where the path may be a template. <https://github.com/bckohan/django-render-static/issues/156>`_
9+
510
v3.0.1 (15-JUL-2024)
611
====================
712

pyproject.toml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
[tool.poetry]
22
name = "django-render-static"
3-
version = "3.0.1"
3+
version = "3.1.0"
44
description = "Use Django's template engine to render static files at deployment or package time. Includes transpilers for extending Django's url reversal and enums to JavaScript."
55
authors = ["Brian Kohan <[email protected]>"]
66
license = "MIT"

render_static/__init__.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -7,7 +7,7 @@
77
88
"""
99

10-
VERSION = (3, 0, 1)
10+
VERSION = (3, 1, 0)
1111

1212
__title__ = "Django Render Static"
1313
__version__ = ".".join(str(i) for i in VERSION)

render_static/engine.py

Lines changed: 62 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,7 @@
11
import os
22
from collections import Counter, namedtuple
33
from pathlib import Path
4+
from shutil import copy2
45
from typing import Callable, Dict, Generator, List, Optional, Tuple, Union, cast
56

67
from django.conf import settings
@@ -453,6 +454,8 @@ def render_to_disk(
453454
first_engine: bool = False,
454455
first_loader: bool = False,
455456
first_preference: bool = False,
457+
exclude: Optional[List[Path]] = None,
458+
render_contents: bool = True,
456459
) -> List[Render]:
457460
"""
458461
Wrap render_each generator function and return the whole list of
@@ -480,6 +483,12 @@ def render_to_disk(
480483
first_loader will render only the first preference(s) of the first
481484
loader. Preferences are loader specific and documented on the
482485
loader.
486+
:param exclude: A list of template paths to exclude. If the path is a
487+
directory, any template below that directory will be excluded. This
488+
parameter only makes sense to use if your selector is a glob pattern.
489+
:param render_contents: If False, do not render the contents of the template.
490+
If the destination path is a template it will still be rendered against
491+
the context to produce the final path.
483492
:return: Render object for all the template(s) rendered to disk
484493
:raises TemplateDoesNotExist: if no template by the given name is found
485494
:raises ImproperlyConfigured: if not enough information was given to
@@ -494,6 +503,8 @@ def render_to_disk(
494503
first_engine=first_engine,
495504
first_loader=first_loader,
496505
first_preference=first_preference,
506+
exclude=exclude,
507+
render_contents=render_contents,
497508
)
498509
]
499510

@@ -504,6 +515,7 @@ def find(
504515
first_engine: bool = False,
505516
first_loader: bool = False,
506517
first_preference: bool = False,
518+
exclude: Optional[List[Path]] = None,
507519
) -> Generator[Render, None, None]:
508520
"""
509521
Search for all templates that match the given selectors and yield
@@ -514,6 +526,9 @@ def find(
514526
:param first_engine: See render_each
515527
:param first_loader: See render_each
516528
:param first_preference: See render_each
529+
:param exclude: A list of template paths to exclude. If the path is a
530+
directory, any template below that directory will be excluded. This
531+
parameter only makes sense to use if your selector is a glob pattern.
517532
:yield: Render objects for each template to disk
518533
:raises TemplateDoesNotExist: if no template by the given name is found
519534
"""
@@ -535,6 +550,7 @@ def find(
535550
first_engine=first_engine,
536551
first_loader=first_loader,
537552
first_preference=first_preference,
553+
exclude=exclude,
538554
)
539555

540556
def search(
@@ -566,6 +582,8 @@ def render_each(
566582
first_engine: bool = False,
567583
first_loader: bool = False,
568584
first_preference: bool = False,
585+
exclude: Optional[List[Path]] = None,
586+
render_contents: bool = True,
569587
) -> Generator[Render, None, None]:
570588
"""
571589
A generator function that renders all selected templates of the highest
@@ -597,6 +615,12 @@ def render_each(
597615
first_loader will render only the first preference(s) of the first
598616
loader. Preferences are loader specific and documented on the
599617
loader.
618+
:param exclude: A list of template paths to exclude. If the path is a
619+
directory, any template below that directory will be excluded. This
620+
parameter only makes sense to use if your selector is a glob pattern.
621+
:param render_contents: If False, do not render the contents of the template.
622+
If the destination path is a template it will still be rendered against
623+
the context to produce the final path.
600624
:yield: Render objects for each template to disk
601625
:raises TemplateDoesNotExist: if no template by the given name is found
602626
:raises ImproperlyConfigured: if not enough information was given to
@@ -611,6 +635,7 @@ def render_each(
611635
first_engine=first_engine,
612636
first_loader=first_loader,
613637
first_preference=first_preference,
638+
exclude=exclude,
614639
):
615640
ctx = render.config.context.copy()
616641
if context is not None:
@@ -623,12 +648,20 @@ def render_each(
623648
os.makedirs(str(dest), exist_ok=True)
624649
else:
625650
os.makedirs(Path(dest or "").parent, exist_ok=True)
626-
with open(str(dest), "w", encoding="UTF-8") as out:
627-
out.write(render.template.render(r_ctx))
651+
if render_contents:
652+
with open(str(dest), "w", encoding="UTF-8") as out:
653+
out.write(render.template.render(r_ctx))
654+
else:
655+
copy2(Path(render.template.origin.name), Path(dest))
628656
yield render
629657

630658
def resolve_renderings(
631-
self, selector: str, config: TemplateConfig, batch: bool, **kwargs
659+
self,
660+
selector: str,
661+
config: TemplateConfig,
662+
batch: bool,
663+
exclude: Optional[List[Path]] = None,
664+
**kwargs,
632665
) -> Generator[Render, None, None]:
633666
"""
634667
Resolve the given parameters to a or a set of Render objects containing
@@ -637,11 +670,23 @@ def resolve_renderings(
637670
:param selector: The template selector (name string)
638671
:param config: The TemplateConfig to apply to the selector.
639672
:param batch: True if this is a batch rendering, false otherwise.
673+
:param exclude: A list of template paths to exclude. If the path is a
674+
directory, any template below that directory will be excluded. This
675+
parameter only makes sense to use if your selector is a glob pattern.
640676
:param kwargs: Pass through parameters from render_each
641677
:yield: Render objects
642678
"""
643679
templates: Dict[str, DjangoTemplate] = {}
644680
chain = []
681+
682+
excluded_dirs = []
683+
excluded_files = []
684+
for xcl in exclude or []:
685+
if xcl.is_dir():
686+
excluded_dirs.append(xcl.absolute())
687+
else:
688+
excluded_files.append(xcl.absolute())
689+
645690
for engine in self.all():
646691
try:
647692
for template_name in engine.select_templates(
@@ -675,6 +720,20 @@ def resolve_renderings(
675720
raise TemplateDoesNotExist(selector, chain=chain)
676721

677722
for _, template in templates.items():
723+
if any(
724+
(
725+
Path(template.origin.name).absolute().is_relative_to(excl)
726+
for excl in excluded_dirs
727+
)
728+
):
729+
continue
730+
if any(
731+
(
732+
Path(template.origin.name).absolute() == excl
733+
for excl in excluded_files
734+
)
735+
):
736+
continue
678737
yield Render(
679738
selector=selector,
680739
config=config,

render_static/management/commands/renderstatic.py

Lines changed: 24 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -151,6 +151,28 @@ def handle(
151151
),
152152
),
153153
] = False,
154+
exclude: Annotated[
155+
t.List[Path],
156+
Option(
157+
"--exclude",
158+
"-e",
159+
help=_(
160+
"Exclude these files from rendering, or any files at or below "
161+
"this directory."
162+
),
163+
shell_complete=complete_path,
164+
),
165+
] = [],
166+
no_render_contents: Annotated[
167+
bool,
168+
Option(
169+
"--no-render-contents",
170+
help=_(
171+
"Do not render the contents of the files. If paths are "
172+
"templates, destinations will still be rendered."
173+
),
174+
),
175+
] = False,
154176
):
155177
engine = StaticTemplateEngine()
156178

@@ -171,6 +193,8 @@ def handle(
171193
first_engine=first_engine,
172194
first_loader=first_loader,
173195
first_preference=first_preference,
196+
exclude=exclude,
197+
render_contents=not no_render_contents,
174198
):
175199
self.stdout.write(
176200
self.style.SUCCESS(_("Rendered {render}.").format(render=render))

tests/batch_templates/file.txt

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
file.txt: {{ variable }}
Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
file1_1.txt: {{ variable }}
Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
file1_2.txt: {{ variable }}
Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
file2_1.txt: {{ variable }}

0 commit comments

Comments
 (0)