Skip to content

Commit af2f650

Browse files
committed
Implement walkdir. fixes #8814
1 parent 1079755 commit af2f650

File tree

3 files changed

+141
-0
lines changed

3 files changed

+141
-0
lines changed

base/exports.jl

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1298,6 +1298,7 @@ export
12981298
tempname,
12991299
touch,
13001300
uperm,
1301+
walkdir,
13011302

13021303
# external processes ## TODO: whittle down these exports.
13031304
detach,

base/file.jl

Lines changed: 60 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -260,3 +260,63 @@ function readdir(path::AbstractString)
260260
end
261261

262262
readdir() = readdir(".")
263+
264+
"""
265+
walkdir(dir; topdown=true, follow_symlinks=false, onerror=throw)
266+
267+
The walkdir method return an iterator that walks the directory tree of a directory. The iterator returns a tuple containing
268+
`(rootpath, dirs, files)`. The directory tree can be traversed top-down or bottom-up. If walkdir encounters a SystemError
269+
it will raise the error. A custom error handling function can be provided through `onerror` keyword argument, the function
270+
is called with a SystemError as argument.
271+
272+
for (root, dirs, files) in walkdir(".")
273+
println("Directories in \$root")
274+
for dir in dirs
275+
println(joinpath(root, dir)) # path to directories
276+
end
277+
println("Files in \$root")
278+
for file in files
279+
println(joinpath(root, file)) # path to files
280+
end
281+
end
282+
283+
"""
284+
function walkdir(root; topdown=true, follow_symlinks=false, onerror=throw)
285+
content = nothing
286+
try
287+
content = readdir(root)
288+
catch err
289+
isa(err, SystemError) || throw(err)
290+
onerror(err)
291+
#Need to return an empty task to skip the current root folder
292+
return Task(()->())
293+
end
294+
dirs = Array(eltype(content), 0)
295+
files = Array(eltype(content), 0)
296+
for name in content
297+
if isdir(joinpath(root, name))
298+
push!(dirs, name)
299+
else
300+
push!(files, name)
301+
end
302+
end
303+
304+
function _it()
305+
if topdown
306+
produce(root, dirs, files)
307+
end
308+
for dir in dirs
309+
path = joinpath(root,dir)
310+
if follow_symlinks || !islink(path)
311+
for (root_l, dirs_l, files_l) in walkdir(path, topdown=topdown, follow_symlinks=follow_symlinks, onerror=onerror)
312+
produce(root_l, dirs_l, files_l)
313+
end
314+
end
315+
end
316+
if !topdown
317+
produce(root, dirs, files)
318+
end
319+
end
320+
Task(_it)
321+
end
322+

test/file.jl

Lines changed: 80 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -855,6 +855,86 @@ end
855855
@test_throws ArgumentError download("good", "ba\0d")
856856
@test_throws ArgumentError download("ba\0d", "good")
857857

858+
###################
859+
# walkdir #
860+
###################
861+
862+
dir = mktempdir()
863+
cd(dir) do
864+
for i=1:2
865+
mkdir("sub_dir$i")
866+
open("file$i", "w") do f end
867+
868+
mkdir(joinpath("sub_dir1", "subsub_dir$i"))
869+
open(joinpath("sub_dir1", "file$i"), "w") do f end
870+
end
871+
open(joinpath("sub_dir2", "file_dir2"), "w")
872+
has_symlinks = @unix? true : (Base.windows_version() >= Base.WINDOWS_VISTA_VER)
873+
follow_symlink_vec = has_symlinks ? [true, false] : [false]
874+
has_symlinks && symlink("sub_dir2", joinpath("sub_dir1", "link"))
875+
for follow_symlinks in follow_symlink_vec
876+
task = walkdir(".", follow_symlinks=follow_symlinks)
877+
root, dirs, files = consume(task)
878+
@test root == "."
879+
@test dirs == ["sub_dir1", "sub_dir2"]
880+
@test files == ["file1", "file2"]
881+
882+
root, dirs, files = consume(task)
883+
@test root == joinpath(".", "sub_dir1")
884+
@test dirs == (has_symlinks ? ["link", "subsub_dir1", "subsub_dir2"] : ["subsub_dir1", "subsub_dir2"])
885+
@test files == ["file1", "file2"]
886+
887+
root, dirs, files = consume(task)
888+
if follow_symlinks
889+
@test root == joinpath(".", "sub_dir1", "link")
890+
@test dirs == []
891+
@test files == ["file_dir2"]
892+
root, dirs, files = consume(task)
893+
end
894+
for i=1:2
895+
@test root == joinpath(".", "sub_dir1", "subsub_dir$i")
896+
@test dirs == []
897+
@test files == []
898+
root, dirs, files = consume(task)
899+
end
900+
901+
@test root == joinpath(".", "sub_dir2")
902+
@test dirs == []
903+
@test files == ["file_dir2"]
904+
end
905+
906+
for follow_symlinks in follow_symlink_vec
907+
task = walkdir(".", follow_symlinks=follow_symlinks, topdown=false)
908+
root, dirs, files = consume(task)
909+
if follow_symlinks
910+
@test root == joinpath(".", "sub_dir1", "link")
911+
@test dirs == []
912+
@test files == ["file_dir2"]
913+
root, dirs, files = consume(task)
914+
end
915+
for i=1:2
916+
@test root == joinpath(".", "sub_dir1", "subsub_dir$i")
917+
@test dirs == []
918+
@test files == []
919+
root, dirs, files = consume(task)
920+
end
921+
@test root == joinpath(".", "sub_dir1")
922+
@test dirs == (has_symlinks ? ["link", "subsub_dir1", "subsub_dir2"] : ["subsub_dir1", "subsub_dir2"])
923+
@test files == ["file1", "file2"]
924+
925+
root, dirs, files = consume(task)
926+
@test root == joinpath(".", "sub_dir2")
927+
@test dirs == []
928+
@test files == ["file_dir2"]
929+
930+
root, dirs, files = consume(task)
931+
@test root == "."
932+
@test dirs == ["sub_dir1", "sub_dir2"]
933+
@test files == ["file1", "file2"]
934+
end
935+
end
936+
rm(dir, recursive=true)
937+
858938
############
859939
# Clean up #
860940
############

0 commit comments

Comments
 (0)