11import os
22from collections import Counter , namedtuple
33from pathlib import Path
4+ from shutil import copy2
45from typing import Callable , Dict , Generator , List , Optional , Tuple , Union , cast
56
67from 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 ,
0 commit comments