@@ -1822,6 +1822,163 @@ def test_copy_empty(self):
1822
1822
self .assertTrue (target .exists ())
1823
1823
self .assertEqual (target .read_bytes (), b'' )
1824
1824
1825
+ def test_copytree_simple (self ):
1826
+ base = self .cls (self .base )
1827
+ source = base / 'dirC'
1828
+ target = base / 'copyC'
1829
+ source .copytree (target )
1830
+ self .assertTrue (target .is_dir ())
1831
+ self .assertTrue (target .joinpath ('dirD' ).is_dir ())
1832
+ self .assertTrue (target .joinpath ('dirD' , 'fileD' ).is_file ())
1833
+ self .assertEqual (target .joinpath ('dirD' , 'fileD' ).read_text (),
1834
+ "this is file D\n " )
1835
+ self .assertTrue (target .joinpath ('fileC' ).is_file ())
1836
+ self .assertTrue (target .joinpath ('fileC' ).read_text (),
1837
+ "this is file C\n " )
1838
+
1839
+ def test_copytree_complex (self , follow_symlinks = True ):
1840
+ def ordered_walk (path ):
1841
+ for dirpath , dirnames , filenames in path .walk (follow_symlinks = follow_symlinks ):
1842
+ dirnames .sort ()
1843
+ filenames .sort ()
1844
+ yield dirpath , dirnames , filenames
1845
+ base = self .cls (self .base )
1846
+ source = base / 'dirC'
1847
+
1848
+ if self .can_symlink :
1849
+ # Add some symlinks
1850
+ source .joinpath ('linkC' ).symlink_to ('fileC' )
1851
+ source .joinpath ('linkD' ).symlink_to ('dirD' )
1852
+
1853
+ # Perform the copy
1854
+ target = base / 'copyC'
1855
+ source .copytree (target , follow_symlinks = follow_symlinks )
1856
+
1857
+ # Compare the source and target trees
1858
+ source_walk = ordered_walk (source )
1859
+ target_walk = ordered_walk (target )
1860
+ for source_item , target_item in zip (source_walk , target_walk , strict = True ):
1861
+ self .assertEqual (source_item [0 ].relative_to (source ),
1862
+ target_item [0 ].relative_to (target )) # dirpath
1863
+ self .assertEqual (source_item [1 ], target_item [1 ]) # dirnames
1864
+ self .assertEqual (source_item [2 ], target_item [2 ]) # filenames
1865
+ # Compare files and symlinks
1866
+ for filename in source_item [2 ]:
1867
+ source_file = source_item [0 ].joinpath (filename )
1868
+ target_file = target_item [0 ].joinpath (filename )
1869
+ if follow_symlinks or not source_file .is_symlink ():
1870
+ # Regular file.
1871
+ self .assertEqual (source_file .read_bytes (), target_file .read_bytes ())
1872
+ elif source_file .is_dir ():
1873
+ # Symlink to directory.
1874
+ self .assertTrue (target_file .is_dir ())
1875
+ self .assertEqual (source_file .readlink (), target_file .readlink ())
1876
+ else :
1877
+ # Symlink to file.
1878
+ self .assertEqual (source_file .read_bytes (), target_file .read_bytes ())
1879
+ self .assertEqual (source_file .readlink (), target_file .readlink ())
1880
+
1881
+ def test_copytree_complex_follow_symlinks_false (self ):
1882
+ self .test_copytree_complex (follow_symlinks = False )
1883
+
1884
+ def test_copytree_to_existing_directory (self ):
1885
+ base = self .cls (self .base )
1886
+ source = base / 'dirC'
1887
+ target = base / 'copyC'
1888
+ target .mkdir ()
1889
+ target .joinpath ('dirD' ).mkdir ()
1890
+ self .assertRaises (FileExistsError , source .copytree , target )
1891
+
1892
+ def test_copytree_to_existing_directory_dirs_exist_ok (self ):
1893
+ base = self .cls (self .base )
1894
+ source = base / 'dirC'
1895
+ target = base / 'copyC'
1896
+ target .mkdir ()
1897
+ target .joinpath ('dirD' ).mkdir ()
1898
+ source .copytree (target , dirs_exist_ok = True )
1899
+ self .assertTrue (target .is_dir ())
1900
+ self .assertTrue (target .joinpath ('dirD' ).is_dir ())
1901
+ self .assertTrue (target .joinpath ('dirD' , 'fileD' ).is_file ())
1902
+ self .assertEqual (target .joinpath ('dirD' , 'fileD' ).read_text (),
1903
+ "this is file D\n " )
1904
+ self .assertTrue (target .joinpath ('fileC' ).is_file ())
1905
+ self .assertTrue (target .joinpath ('fileC' ).read_text (),
1906
+ "this is file C\n " )
1907
+
1908
+ def test_copytree_file (self ):
1909
+ base = self .cls (self .base )
1910
+ source = base / 'fileA'
1911
+ target = base / 'copyA'
1912
+ self .assertRaises (NotADirectoryError , source .copytree , target )
1913
+
1914
+ def test_copytree_file_on_error (self ):
1915
+ base = self .cls (self .base )
1916
+ source = base / 'fileA'
1917
+ target = base / 'copyA'
1918
+ errors = []
1919
+ source .copytree (target , on_error = errors .append )
1920
+ self .assertEqual (len (errors ), 1 )
1921
+ self .assertIsInstance (errors [0 ], NotADirectoryError )
1922
+
1923
+ def test_copytree_ignore_false (self ):
1924
+ base = self .cls (self .base )
1925
+ source = base / 'dirC'
1926
+ target = base / 'copyC'
1927
+ ignores = []
1928
+ def ignore_false (path ):
1929
+ ignores .append (path )
1930
+ return False
1931
+ source .copytree (target , ignore = ignore_false )
1932
+ self .assertEqual (set (ignores ), {
1933
+ source / 'dirD' ,
1934
+ source / 'dirD' / 'fileD' ,
1935
+ source / 'fileC' ,
1936
+ source / 'novel.txt' ,
1937
+ })
1938
+ self .assertTrue (target .is_dir ())
1939
+ self .assertTrue (target .joinpath ('dirD' ).is_dir ())
1940
+ self .assertTrue (target .joinpath ('dirD' , 'fileD' ).is_file ())
1941
+ self .assertEqual (target .joinpath ('dirD' , 'fileD' ).read_text (),
1942
+ "this is file D\n " )
1943
+ self .assertTrue (target .joinpath ('fileC' ).is_file ())
1944
+ self .assertTrue (target .joinpath ('fileC' ).read_text (),
1945
+ "this is file C\n " )
1946
+
1947
+ def test_copytree_ignore_true (self ):
1948
+ base = self .cls (self .base )
1949
+ source = base / 'dirC'
1950
+ target = base / 'copyC'
1951
+ ignores = []
1952
+ def ignore_true (path ):
1953
+ ignores .append (path )
1954
+ return True
1955
+ source .copytree (target , ignore = ignore_true )
1956
+ self .assertEqual (set (ignores ), {
1957
+ source / 'dirD' ,
1958
+ source / 'fileC' ,
1959
+ source / 'novel.txt' ,
1960
+ })
1961
+ self .assertTrue (target .is_dir ())
1962
+ self .assertFalse (target .joinpath ('dirD' ).exists ())
1963
+ self .assertFalse (target .joinpath ('fileC' ).exists ())
1964
+ self .assertFalse (target .joinpath ('novel.txt' ).exists ())
1965
+
1966
+ @needs_symlinks
1967
+ def test_copytree_dangling_symlink (self ):
1968
+ base = self .cls (self .base )
1969
+ source = base / 'source'
1970
+ target = base / 'target'
1971
+
1972
+ source .mkdir ()
1973
+ source .joinpath ('link' ).symlink_to ('nonexistent' )
1974
+
1975
+ self .assertRaises (FileNotFoundError , source .copytree , target )
1976
+
1977
+ target2 = base / 'target2'
1978
+ source .copytree (target2 , follow_symlinks = False )
1979
+ self .assertTrue (target2 .joinpath ('link' ).is_symlink ())
1980
+ self .assertEqual (target2 .joinpath ('link' ).readlink (), self .cls ('nonexistent' ))
1981
+
1825
1982
def test_iterdir (self ):
1826
1983
P = self .cls
1827
1984
p = P (self .base )
0 commit comments