@@ -2478,6 +2478,203 @@ def test_complex_symlinks_relative(self):
2478
2478
def test_complex_symlinks_relative_dot_dot (self ):
2479
2479
self ._check_complex_symlinks (os .path .join ('dirA' , '..' ))
2480
2480
2481
+ class WalkTests (unittest .TestCase ):
2482
+
2483
+ def setUp (self ):
2484
+ self .addCleanup (os_helper .rmtree , os_helper .TESTFN )
2485
+
2486
+ # Build:
2487
+ # TESTFN/
2488
+ # TEST1/ a file kid and two directory kids
2489
+ # tmp1
2490
+ # SUB1/ a file kid and a directory kid
2491
+ # tmp2
2492
+ # SUB11/ no kids
2493
+ # SUB2/ a file kid and a dirsymlink kid
2494
+ # tmp3
2495
+ # SUB21/ not readable
2496
+ # tmp5
2497
+ # link/ a symlink to TEST2
2498
+ # broken_link
2499
+ # broken_link2
2500
+ # broken_link3
2501
+ # TEST2/
2502
+ # tmp4 a lone file
2503
+ self .walk_path = pathlib .Path (os_helper .TESTFN , "TEST1" )
2504
+ self .sub1_path = self .walk_path / "SUB1"
2505
+ self .sub11_path = self .sub1_path / "SUB11"
2506
+ self .sub2_path = self .walk_path / "SUB2"
2507
+ sub21_path = self .sub2_path / "SUB21"
2508
+ tmp1_path = self .walk_path / "tmp1"
2509
+ tmp2_path = self .sub1_path / "tmp2"
2510
+ tmp3_path = self .sub2_path / "tmp3"
2511
+ tmp5_path = sub21_path / "tmp3"
2512
+ self .link_path = self .sub2_path / "link"
2513
+ t2_path = pathlib .Path (os_helper .TESTFN , "TEST2" )
2514
+ tmp4_path = pathlib .Path (os_helper .TESTFN , "TEST2" , "tmp4" )
2515
+ broken_link_path = self .sub2_path / "broken_link"
2516
+ broken_link2_path = self .sub2_path / "broken_link2"
2517
+ broken_link3_path = self .sub2_path / "broken_link3"
2518
+
2519
+ os .makedirs (self .sub11_path )
2520
+ os .makedirs (self .sub2_path )
2521
+ os .makedirs (sub21_path )
2522
+ os .makedirs (t2_path )
2523
+
2524
+ for path in tmp1_path , tmp2_path , tmp3_path , tmp4_path , tmp5_path :
2525
+ with open (path , "x" , encoding = 'utf-8' ) as f :
2526
+ f .write (f"I'm { path } and proud of it. Blame test_pathlib.\n " )
2527
+
2528
+ if os_helper .can_symlink ():
2529
+ os .symlink (os .path .abspath (t2_path ), self .link_path )
2530
+ os .symlink ('broken' , broken_link_path , True )
2531
+ os .symlink (pathlib .Path ('tmp3' , 'broken' ), broken_link2_path , True )
2532
+ os .symlink (pathlib .Path ('SUB21' , 'tmp5' ), broken_link3_path , True )
2533
+ self .sub2_tree = (self .sub2_path , ["SUB21" ],
2534
+ ["broken_link" , "broken_link2" , "broken_link3" ,
2535
+ "link" , "tmp3" ])
2536
+ else :
2537
+ self .sub2_tree = (self .sub2_path , ["SUB21" ], ["tmp3" ])
2538
+
2539
+ if not is_emscripten :
2540
+ # Emscripten fails with inaccessible directories.
2541
+ os .chmod (sub21_path , 0 )
2542
+ try :
2543
+ os .listdir (sub21_path )
2544
+ except PermissionError :
2545
+ self .addCleanup (os .chmod , sub21_path , stat .S_IRWXU )
2546
+ else :
2547
+ os .chmod (sub21_path , stat .S_IRWXU )
2548
+ os .unlink (tmp5_path )
2549
+ os .rmdir (sub21_path )
2550
+ del self .sub2_tree [1 ][:1 ]
2551
+
2552
+ def test_walk_topdown (self ):
2553
+ all = list (self .walk_path .walk ())
2554
+
2555
+ self .assertEqual (len (all ), 4 )
2556
+ # We can't know which order SUB1 and SUB2 will appear in.
2557
+ # Not flipped: TESTFN, SUB1, SUB11, SUB2
2558
+ # flipped: TESTFN, SUB2, SUB1, SUB11
2559
+ flipped = all [0 ][1 ][0 ] != "SUB1"
2560
+ all [0 ][1 ].sort ()
2561
+ all [3 - 2 * flipped ][- 1 ].sort ()
2562
+ all [3 - 2 * flipped ][1 ].sort ()
2563
+ self .assertEqual (all [0 ], (self .walk_path , ["SUB1" , "SUB2" ], ["tmp1" ]))
2564
+ self .assertEqual (all [1 + flipped ], (self .sub1_path , ["SUB11" ], ["tmp2" ]))
2565
+ self .assertEqual (all [2 + flipped ], (self .sub11_path , [], []))
2566
+ self .assertEqual (all [3 - 2 * flipped ], self .sub2_tree )
2567
+
2568
+ def test_walk_prune (self , walk_path = None ):
2569
+ if walk_path is None :
2570
+ walk_path = self .walk_path
2571
+ # Prune the search.
2572
+ all = []
2573
+ for root , dirs , files in walk_path .walk ():
2574
+ all .append ((root , dirs , files ))
2575
+ if 'SUB1' in dirs :
2576
+ # Note that this also mutates the dirs we appended to all!
2577
+ dirs .remove ('SUB1' )
2578
+
2579
+ self .assertEqual (len (all ), 2 )
2580
+ self .assertEqual (all [0 ], (self .walk_path , ["SUB2" ], ["tmp1" ]))
2581
+
2582
+ all [1 ][- 1 ].sort ()
2583
+ all [1 ][1 ].sort ()
2584
+ self .assertEqual (all [1 ], self .sub2_tree )
2585
+
2586
+ def test_file_like_path (self ):
2587
+ self .test_walk_prune (FakePath (self .walk_path ).__fspath__ ())
2588
+
2589
+ def test_walk_bottom_up (self ):
2590
+ all = list (self .walk_path .walk ( top_down = False ))
2591
+
2592
+ self .assertEqual (len (all ), 4 , all )
2593
+ # We can't know which order SUB1 and SUB2 will appear in.
2594
+ # Not flipped: SUB11, SUB1, SUB2, TESTFN
2595
+ # flipped: SUB2, SUB11, SUB1, TESTFN
2596
+ flipped = all [3 ][1 ][0 ] != "SUB1"
2597
+ all [3 ][1 ].sort ()
2598
+ all [2 - 2 * flipped ][- 1 ].sort ()
2599
+ all [2 - 2 * flipped ][1 ].sort ()
2600
+ self .assertEqual (all [3 ],
2601
+ (self .walk_path , ["SUB1" , "SUB2" ], ["tmp1" ]))
2602
+ self .assertEqual (all [flipped ],
2603
+ (self .sub11_path , [], []))
2604
+ self .assertEqual (all [flipped + 1 ],
2605
+ (self .sub1_path , ["SUB11" ], ["tmp2" ]))
2606
+ self .assertEqual (all [2 - 2 * flipped ],
2607
+ self .sub2_tree )
2608
+
2609
+ @os_helper .skip_unless_symlink
2610
+ def test_walk_follow_symlinks (self ):
2611
+ walk_it = self .walk_path .walk (follow_symlinks = True )
2612
+ for root , dirs , files in walk_it :
2613
+ if root == self .link_path :
2614
+ self .assertEqual (dirs , [])
2615
+ self .assertEqual (files , ["tmp4" ])
2616
+ break
2617
+ else :
2618
+ self .fail ("Didn't follow symlink with follow_symlinks=True" )
2619
+
2620
+ def test_walk_symlink_location (self ):
2621
+ # Tests whether symlinks end up in filenames or dirnames depending
2622
+ # on the `follow_symlinks` argument.
2623
+ walk_it = self .walk_path .walk (follow_symlinks = False )
2624
+ for root , dirs , files in walk_it :
2625
+ if root == self .sub2_path :
2626
+ self .assertIn ("link" , files )
2627
+ break
2628
+ else :
2629
+ self .fail ("symlink not found" )
2630
+
2631
+ walk_it = self .walk_path .walk (follow_symlinks = True )
2632
+ for root , dirs , files in walk_it :
2633
+ if root == self .sub2_path :
2634
+ self .assertIn ("link" , dirs )
2635
+ break
2636
+
2637
+ def test_walk_bad_dir (self ):
2638
+ errors = []
2639
+ walk_it = self .walk_path .walk (on_error = errors .append )
2640
+ root , dirs , files = next (walk_it )
2641
+ self .assertEqual (errors , [])
2642
+ dir1 = 'SUB1'
2643
+ path1 = root / dir1
2644
+ path1new = (root / dir1 ).with_suffix (".new" )
2645
+ path1 .rename (path1new )
2646
+ try :
2647
+ roots = [r for r , _ , _ in walk_it ]
2648
+ self .assertTrue (errors )
2649
+ self .assertNotIn (path1 , roots )
2650
+ self .assertNotIn (path1new , roots )
2651
+ for dir2 in dirs :
2652
+ if dir2 != dir1 :
2653
+ self .assertIn (root / dir2 , roots )
2654
+ finally :
2655
+ path1new .rename (path1 )
2656
+
2657
+ def test_walk_many_open_files (self ):
2658
+ depth = 30
2659
+ base = pathlib .Path (os_helper .TESTFN , 'deep' )
2660
+ path = pathlib .Path (base , * (['d' ]* depth ))
2661
+ path .mkdir (parents = True )
2662
+
2663
+ iters = [base .walk (top_down = False ) for _ in range (100 )]
2664
+ for i in range (depth + 1 ):
2665
+ expected = (path , ['d' ] if i else [], [])
2666
+ for it in iters :
2667
+ self .assertEqual (next (it ), expected )
2668
+ path = path .parent
2669
+
2670
+ iters = [base .walk (top_down = True ) for _ in range (100 )]
2671
+ path = base
2672
+ for i in range (depth + 1 ):
2673
+ expected = (path , ['d' ] if i < depth else [], [])
2674
+ for it in iters :
2675
+ self .assertEqual (next (it ), expected )
2676
+ path = path / 'd'
2677
+
2481
2678
2482
2679
class PathTest (_BasePathTest , unittest .TestCase ):
2483
2680
cls = pathlib .Path
0 commit comments