@@ -1822,6 +1822,163 @@ def test_copy_empty(self):
18221822 self .assertTrue (target .exists ())
18231823 self .assertEqual (target .read_bytes (), b'' )
18241824
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+
18251982 def test_iterdir (self ):
18261983 P = self .cls
18271984 p = P (self .base )
0 commit comments