@@ -583,7 +583,7 @@ def resolve_package_path(path: Path) -> Optional[Path]:
583
583
584
584
585
585
def visit (
586
- path : str , recurse : Callable [["os.DirEntry[str]" ], bool ]
586
+ path : Union [ str , "os.PathLike[str]" ] , recurse : Callable [["os.DirEntry[str]" ], bool ]
587
587
) -> Iterator ["os.DirEntry[str]" ]:
588
588
"""Walk a directory recursively, in breadth-first order.
589
589
@@ -657,3 +657,21 @@ def bestrelpath(directory: Path, dest: Path) -> str:
657
657
# Forward from base to dest.
658
658
* reldest .parts ,
659
659
)
660
+
661
+
662
+ # Originates from py. path.local.copy(), with siginficant trims and adjustments.
663
+ # TODO(py38): Replace with shutil.copytree(..., symlinks=True, dirs_exist_ok=True)
664
+ def copytree (source : Path , target : Path ) -> None :
665
+ """Recursively copy a source directory to target."""
666
+ assert source .is_dir ()
667
+ for entry in visit (source , recurse = lambda entry : not entry .is_symlink ()):
668
+ x = Path (entry )
669
+ relpath = x .relative_to (source )
670
+ newx = target / relpath
671
+ newx .parent .mkdir (exist_ok = True )
672
+ if x .is_symlink ():
673
+ newx .symlink_to (os .readlink (x ))
674
+ elif x .is_file ():
675
+ shutil .copyfile (x , newx )
676
+ elif x .is_dir ():
677
+ newx .mkdir (exist_ok = True )
0 commit comments