Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

RFC: Change 'readdir' to return an iterator. #27450

Closed
wants to merge 4 commits into from
Closed
Show file tree
Hide file tree
Changes from 1 commit
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
54 changes: 30 additions & 24 deletions base/file.jl
Original file line number Diff line number Diff line change
Expand Up @@ -572,14 +572,41 @@ struct uv_dirent_t
typ::Cint
end

struct ReadDirIter
Copy link
Sponsor Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This seems unnecessarily abbreviated. How about ReadDirIterator?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Sure

path::String
end

function Base.iterate(iter::ReadDirIter)
uv_readdir_req = zeros(UInt8, ccall(:jl_sizeof_uv_fs_t, Int32, ()))
path = iter.path
err = ccall(:uv_fs_scandir, Int32, (Ptr{Cvoid}, Ptr{UInt8}, Cstring, Cint, Ptr{Cvoid}),
eventloop(), uv_readdir_req, path, 0, C_NULL)
err < 0 && throw(SystemError("unable to read directory $path", -err))
return iterate(iter, uv_readdir_req)
end

function Base.iterate(d::ReadDirIter, uv_readdir_req::Vector{UInt8})
ent = Ref{uv_dirent_t}()
status = ccall(:uv_fs_scandir_next, Cint, (Ptr{Cvoid}, Ptr{uv_dirent_t}), uv_readdir_req, ent)
if status == Base.UV_EOF
ccall(:jl_uv_fs_req_cleanup, Cvoid, (Ptr{UInt8},), uv_readdir_req)
return nothing
end
path = unsafe_string(ent[].name)
return (path, uv_readdir_req)
end

Base.IteratorSize(::Type{ReadDirIter}) = Base.SizeUnknown()
Base.eltype(::Type{ReadDirIter}) = String

"""
readdir(dir::AbstractString=".") -> Vector{String}

Return the files and directories in the directory `dir` (or the current working directory if not given).
Return an iterator that generates the files and directories in the directory `dir` (or the current working directory if not given).

# Examples
```julia-repl
julia> readdir("/home/JuliaUser/Projects/julia")
julia> collect(readdir("/home/JuliaUser/Projects/julia"))
34-element Array{String,1}:
".circleci"
".freebsdci.sh"
Expand All @@ -593,28 +620,7 @@ julia> readdir("/home/JuliaUser/Projects/julia")
"usr-staging"
```
"""
function readdir(path::AbstractString)
# Allocate space for uv_fs_t struct
uv_readdir_req = zeros(UInt8, ccall(:jl_sizeof_uv_fs_t, Int32, ()))

# defined in sys.c, to call uv_fs_readdir, which sets errno on error.
err = ccall(:uv_fs_scandir, Int32, (Ptr{Cvoid}, Ptr{UInt8}, Cstring, Cint, Ptr{Cvoid}),
eventloop(), uv_readdir_req, path, 0, C_NULL)
err < 0 && throw(SystemError("unable to read directory $path", -err))
#uv_error("unable to read directory $path", err)

# iterate the listing into entries
entries = String[]
ent = Ref{uv_dirent_t}()
while Base.UV_EOF != ccall(:uv_fs_scandir_next, Cint, (Ptr{Cvoid}, Ptr{uv_dirent_t}), uv_readdir_req, ent)
push!(entries, unsafe_string(ent[].name))
end

# Clean up the request string
ccall(:jl_uv_fs_req_cleanup, Cvoid, (Ptr{UInt8},), uv_readdir_req)

return entries
end
readdir(path::AbstractString) = ReadDirIter(String(path))

readdir() = readdir(".")

Expand Down
2 changes: 1 addition & 1 deletion test/choosetests.jl
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@
using Random, Sockets

const STDLIB_DIR = joinpath(Sys.BINDIR, "..", "share", "julia", "stdlib", "v$(VERSION.major).$(VERSION.minor)")
const STDLIBS = readdir(STDLIB_DIR)
const STDLIBS = collect(readdir(STDLIB_DIR))

"""

Expand Down
14 changes: 7 additions & 7 deletions test/file.jl
Original file line number Diff line number Diff line change
Expand Up @@ -282,8 +282,8 @@ function check_dir(orig_path::AbstractString, copied_path::AbstractString, follo
isdir(orig_path) || throw(ArgumentError("'$orig_path' is not a directory."))
# copied_path must also be a dir.
@test isdir(copied_path)
readir_orig = readdir(orig_path)
readir_copied = readdir(copied_path)
readir_orig = collect(readdir(orig_path))
readir_copied = collect(readdir(copied_path))
@test readir_orig == readir_copied
# check recursive
for name in readir_orig
Expand Down Expand Up @@ -446,8 +446,8 @@ if !Sys.iswindows() || Sys.windows_version() >= Sys.WINDOWS_VISTA_VER
islink(s) && @test readlink(s) == readlink(d)
islink(s) && @test isabspath(readlink(s)) == isabspath(readlink(d))
# all should contain 1 file named "c.txt"
@test "c.txt" in readdir(d)
@test length(readdir(d)) == 1
@test "c.txt" in collect(readdir(d))
@test length(collect(readdir(d))) == 1
end

function mv_check(s, d, d_mv; force=true)
Expand All @@ -465,8 +465,8 @@ if !Sys.iswindows() || Sys.windows_version() >= Sys.WINDOWS_VISTA_VER
islink(s) && @test readlink(s) == readlink(d_mv)
islink(s) && @test isabspath(readlink(s)) == isabspath(readlink(d_mv))
# all should contain 1 file named "c.txt"
@test "c.txt" in readdir(d_mv)
@test length(readdir(d_mv)) == 1
@test "c.txt" in collect(readdir(d_mv))
@test length(collect(readdir(d_mv))) == 1
# d => d_mv same file/dir
@test Base.samefile(stat_d, stat_d_mv)
end
Expand Down Expand Up @@ -509,7 +509,7 @@ if !Sys.iswindows() || Sys.windows_version() >= Sys.WINDOWS_VISTA_VER
# Expect no link because a dir is copied (follow_symlinks=false does not effect this)
@test isdir(d) && !islink(d)
# none should contain any file
@test isempty(readdir(d))
@test isempty(collect(readdir(d)))
end
end
# mv ----------------------------------------------------
Expand Down
2 changes: 1 addition & 1 deletion test/spawn.jl
Original file line number Diff line number Diff line change
Expand Up @@ -476,7 +476,7 @@ if Sys.isunix()
end

# readlines(::Cmd), accidentally broken in #20203
@test sort(readlines(`$lscmd -A`)) == sort(readdir())
@test sort(collect(readlines(`$lscmd -A`))) == sort(collect(readdir()))

# issue #19864 (PR #20497)
let c19864 = readchomp(pipeline(ignorestatus(
Expand Down