53
53
escape_git_path ,
54
54
normalize_git_path ,
55
55
printf ,
56
+ scantree ,
56
57
set_git_alternates ,
57
58
)
58
59
from .types import (
@@ -599,23 +600,41 @@ def match_skip(self) -> Callable[[Path], bool]:
599
600
)
600
601
)
601
602
602
- def _render_file (self , src_abspath : Path ) -> None :
603
+ def _render_template (self ) -> None :
604
+ """Render the template in the subproject root."""
605
+ for src in scantree (str (self .template_copy_root )):
606
+ src_abspath = Path (src .path )
607
+ src_relpath = Path (src_abspath ).relative_to (self .template .local_abspath )
608
+ dst_relpath = self ._render_path (
609
+ Path (src_abspath ).relative_to (self .template_copy_root )
610
+ )
611
+ if dst_relpath is None or self .match_exclude (dst_relpath ):
612
+ continue
613
+ if src .is_symlink () and self .template .preserve_symlinks :
614
+ self ._render_symlink (src_relpath , dst_relpath )
615
+ elif src .is_dir (follow_symlinks = False ):
616
+ self ._render_folder (dst_relpath )
617
+ else :
618
+ self ._render_file (src_relpath , dst_relpath )
619
+
620
+ def _render_file (self , src_relpath : Path , dst_relpath : Path ) -> None :
603
621
"""Render one file.
604
622
605
623
Args:
606
- src_abspath:
607
- The absolute path to the file that will be rendered.
624
+ src_relpath:
625
+ File to be rendered. It must be a path relative to the template
626
+ root.
627
+ dst_relpath:
628
+ File to be created. It must be a path relative to the subproject
629
+ root.
608
630
"""
609
631
# TODO Get from main.render_file()
610
- assert src_abspath .is_absolute ()
611
- src_relpath = src_abspath .relative_to (self .template .local_abspath ).as_posix ()
612
- src_renderpath = src_abspath .relative_to (self .template_copy_root )
613
- dst_relpath = self ._render_path (src_renderpath )
614
- if dst_relpath is None :
615
- return
616
- if src_abspath .name .endswith (self .template .templates_suffix ):
632
+ assert not src_relpath .is_absolute ()
633
+ assert not dst_relpath .is_absolute ()
634
+ src_abspath = self .template .local_abspath / src_relpath
635
+ if src_relpath .name .endswith (self .template .templates_suffix ):
617
636
try :
618
- tpl = self .jinja_env .get_template (src_relpath )
637
+ tpl = self .jinja_env .get_template (src_relpath . as_posix () )
619
638
except UnicodeDecodeError :
620
639
if self .template .templates_suffix :
621
640
# suffix is not empty, re-raise
@@ -626,7 +645,7 @@ def _render_file(self, src_abspath: Path) -> None:
626
645
new_content = tpl .render (** self ._render_context ()).encode ()
627
646
else :
628
647
new_content = src_abspath .read_bytes ()
629
- dst_abspath = Path ( self .subproject .local_abspath , dst_relpath )
648
+ dst_abspath = self .subproject .local_abspath / dst_relpath
630
649
src_mode = src_abspath .stat ().st_mode
631
650
if not self ._render_allowed (dst_relpath , expected_contents = new_content ):
632
651
return
@@ -639,21 +658,23 @@ def _render_file(self, src_abspath: Path) -> None:
639
658
dst_abspath .write_bytes (new_content )
640
659
dst_abspath .chmod (src_mode )
641
660
642
- def _render_symlink (self , src_abspath : Path ) -> None :
661
+ def _render_symlink (self , src_relpath : Path , dst_relpath : Path ) -> None :
643
662
"""Render one symlink.
644
663
645
664
Args:
646
- src_abspath:
647
- Symlink to be rendered. It must be an absolute path within
648
- the template.
665
+ src_relpath:
666
+ Symlink to be rendered. It must be a path relative to the
667
+ template root.
668
+ dst_relpath:
669
+ Symlink to be created. It must be a path relative to the
670
+ subproject root.
649
671
"""
650
- assert src_abspath .is_absolute ()
651
- src_relpath = src_abspath .relative_to (self .template_copy_root )
652
- dst_relpath = self ._render_path (src_relpath )
653
- if dst_relpath is None :
672
+ assert not src_relpath .is_absolute ()
673
+ assert not dst_relpath .is_absolute ()
674
+ if dst_relpath is None or self .match_exclude (dst_relpath ):
654
675
return
655
- dst_abspath = Path (self .subproject .local_abspath , dst_relpath )
656
676
677
+ src_abspath = self .template .local_abspath / src_relpath
657
678
src_target = src_abspath .readlink ()
658
679
if src_abspath .name .endswith (self .template .templates_suffix ):
659
680
dst_target = Path (self ._render_string (str (src_target )))
@@ -668,41 +689,30 @@ def _render_symlink(self, src_abspath: Path) -> None:
668
689
return
669
690
670
691
if not self .pretend :
692
+ dst_abspath = self .subproject .local_abspath / dst_relpath
671
693
# symlink_to doesn't overwrite existing files, so delete it first
672
694
if dst_abspath .is_symlink () or dst_abspath .exists ():
673
695
dst_abspath .unlink ()
696
+ dst_abspath .parent .mkdir (parents = True , exist_ok = True )
674
697
dst_abspath .symlink_to (dst_target )
675
698
if sys .platform == "darwin" :
676
699
# Only macOS supports permissions on symlinks.
677
700
# Other platforms just copy the permission of the target
678
701
src_mode = src_abspath .lstat ().st_mode
679
702
dst_abspath .lchmod (src_mode )
680
703
681
- def _render_folder (self , src_abspath : Path ) -> None :
682
- """Recursively render a folder.
704
+ def _render_folder (self , dst_relpath : Path ) -> None :
705
+ """Create one folder (without content) .
683
706
684
707
Args:
685
- src_abspath :
686
- Folder to be rendered . It must be an absolute path within
687
- the template .
708
+ dst_relpath :
709
+ Folder to be created . It must be a path relative to the
710
+ subproject root .
688
711
"""
689
- assert src_abspath .is_absolute ()
690
- src_relpath = src_abspath .relative_to (self .template_copy_root )
691
- dst_relpath = self ._render_path (src_relpath )
692
- if dst_relpath is None :
693
- return
694
- if not self ._render_allowed (dst_relpath , is_dir = True ):
695
- return
696
- dst_abspath = Path (self .subproject .local_abspath , dst_relpath )
697
- if not self .pretend :
712
+ assert not dst_relpath .is_absolute ()
713
+ if not self .pretend and self ._render_allowed (dst_relpath , is_dir = True ):
714
+ dst_abspath = self .subproject .local_abspath / dst_relpath
698
715
dst_abspath .mkdir (parents = True , exist_ok = True )
699
- for file in src_abspath .iterdir ():
700
- if file .is_symlink () and self .template .preserve_symlinks :
701
- self ._render_symlink (file )
702
- elif file .is_dir ():
703
- self ._render_folder (file )
704
- else :
705
- self ._render_file (file )
706
716
707
717
def _render_path (self , relpath : Path ) -> Path | None :
708
718
"""Render one relative path.
@@ -732,9 +742,6 @@ def _render_path(self, relpath: Path) -> Path | None:
732
742
part = Path (part ).name
733
743
rendered_parts .append (part )
734
744
result = Path (* rendered_parts )
735
- # Skip excluded paths.
736
- if result != Path ("." ) and self .match_exclude (result ):
737
- return None
738
745
if not is_template :
739
746
templated_sibling = (
740
747
self .template .local_abspath
@@ -824,15 +831,14 @@ def run_copy(self) -> None:
824
831
self ._print_message (self .template .message_before_copy )
825
832
self ._ask ()
826
833
was_existing = self .subproject .local_abspath .exists ()
827
- src_abspath = self .template_copy_root
828
834
try :
829
835
if not self .quiet :
830
836
# TODO Unify printing tools
831
837
print (
832
838
f"\n Copying from template version { self .template .version } " ,
833
839
file = sys .stderr ,
834
840
)
835
- self ._render_folder ( src_abspath )
841
+ self ._render_template ( )
836
842
if not self .quiet :
837
843
# TODO Unify printing tools
838
844
print ("" ) # padding space
0 commit comments