From 8d31f334269c6553553cad4220215a03c6da7ab3 Mon Sep 17 00:00:00 2001 From: Florian Date: Wed, 13 Mar 2024 10:04:08 +0100 Subject: [PATCH] Move `isexecutable, isreadable, iswritable` to `filesystem.jl` (#53699) This PR migrates the methods `isexecutable, isreadable, iswritable` from `Sys` to `Base`, but also generates an alias in `Sys` for backwards compatibility. Furthermore, `iswriteable` is renamed to `iswritable` in order to match the already existing `Base.iswritable` method. Suggested in https://github.com/JuliaLang/julia/pull/53320#issuecomment-1989217973. --- base/exports.jl | 1 + base/filesystem.jl | 83 ++++++++++++++++++++++++++++++++++++- base/io.jl | 2 + base/sysinfo.jl | 84 ++------------------------------------ doc/src/base/base.md | 2 +- doc/src/base/io-network.md | 1 + test/file.jl | 12 +++--- 7 files changed, 97 insertions(+), 88 deletions(-) diff --git a/base/exports.jl b/base/exports.jl index 368f336188486..3044f9d162d41 100644 --- a/base/exports.jl +++ b/base/exports.jl @@ -933,6 +933,7 @@ export isblockdev, ischardev, isdir, + isexecutable, isfifo, isfile, islink, diff --git a/base/filesystem.jl b/base/filesystem.jl index ee6fde982caab..36e4bb4596285 100644 --- a/base/filesystem.jl +++ b/base/filesystem.jl @@ -140,7 +140,8 @@ import .Base: IOError, _UVError, _sizeof_uv_fs, check_open, close, eof, eventloop, fd, isopen, bytesavailable, position, read, read!, readavailable, seek, seekend, show, skip, stat, unsafe_read, unsafe_write, write, transcode, uv_error, - setup_stdio, rawhandle, OS_HANDLE, INVALID_OS_HANDLE, windowserror, filesize + setup_stdio, rawhandle, OS_HANDLE, INVALID_OS_HANDLE, windowserror, filesize, + isexecutable, isreadable, iswritable import .Base.RefValue @@ -365,5 +366,85 @@ function touch(f::File) f end +""" + isexecutable(path::String) + +Return `true` if the given `path` has executable permissions. + +!!! note + This permission may change before the user executes `path`, + so it is recommended to execute the file and handle the error if that fails, + rather than calling `isexecutable` first. + +!!! note + Prior to Julia 1.6, this did not correctly interrogate filesystem + ACLs on Windows, therefore it would return `true` for any + file. From Julia 1.6 on, it correctly determines whether the + file is marked as executable or not. + +See also [`ispath`](@ref), [`isreadable`](@ref), [`iswritable`](@ref). +""" +function isexecutable(path::String) + # We use `access()` and `X_OK` to determine if a given path is + # executable by the current user. `X_OK` comes from `unistd.h`. + X_OK = 0x01 + return ccall(:jl_fs_access, Cint, (Cstring, Cint), path, X_OK) == 0 +end +isexecutable(path::AbstractString) = isexecutable(String(path)) + +""" + isreadable(path::String) + +Return `true` if the access permissions for the given `path` permitted reading by the current user. + +!!! note + This permission may change before the user calls `open`, + so it is recommended to just call `open` alone and handle the error if that fails, + rather than calling `isreadable` first. + +!!! note + Currently this function does not correctly interrogate filesystem + ACLs on Windows, therefore it can return wrong results. + +!!! compat "Julia 1.11" + This function requires at least Julia 1.11. + +See also [`ispath`](@ref), [`isexecutable`](@ref), [`iswritable`](@ref). +""" +function isreadable(path::String) + # We use `access()` and `R_OK` to determine if a given path is + # readable by the current user. `R_OK` comes from `unistd.h`. + R_OK = 0x04 + return ccall(:jl_fs_access, Cint, (Cstring, Cint), path, R_OK) == 0 +end +isreadable(path::AbstractString) = isreadable(String(path)) + +""" + iswritable(path::String) + +Return `true` if the access permissions for the given `path` permitted writing by the current user. + +!!! note + This permission may change before the user calls `open`, + so it is recommended to just call `open` alone and handle the error if that fails, + rather than calling `iswritable` first. + +!!! note + Currently this function does not correctly interrogate filesystem + ACLs on Windows, therefore it can return wrong results. + +!!! compat "Julia 1.11" + This function requires at least Julia 1.11. + +See also [`ispath`](@ref), [`isexecutable`](@ref), [`isreadable`](@ref). +""" +function iswritable(path::String) + # We use `access()` and `W_OK` to determine if a given path is + # writeable by the current user. `W_OK` comes from `unistd.h`. + W_OK = 0x02 + return ccall(:jl_fs_access, Cint, (Cstring, Cint), path, W_OK) == 0 +end +iswritable(path::AbstractString) = iswritable(String(path)) + end diff --git a/base/io.jl b/base/io.jl index 7d93bc3e6d9c8..fb883234be4df 100644 --- a/base/io.jl +++ b/base/io.jl @@ -131,6 +131,8 @@ data has already been buffered. The result is a `Vector{UInt8}`. """ function readavailable end +function isexecutable end + """ isreadable(io) -> Bool diff --git a/base/sysinfo.jl b/base/sysinfo.jl index c5744873312d6..9bdff3673477f 100644 --- a/base/sysinfo.jl +++ b/base/sysinfo.jl @@ -34,7 +34,7 @@ export BINDIR, isjsvm, isexecutable, isreadable, - iswriteable, + iswritable, username, which @@ -553,85 +553,9 @@ windows_version const WINDOWS_VISTA_VER = v"6.0" -""" - Sys.isexecutable(path::String) - -Return `true` if the given `path` has executable permissions. - -!!! note - This permission may change before the user executes `path`, - so it is recommended to execute the file and handle the error if that fails, - rather than calling `isexecutable` first. - -!!! note - Prior to Julia 1.6, this did not correctly interrogate filesystem - ACLs on Windows, therefore it would return `true` for any - file. From Julia 1.6 on, it correctly determines whether the - file is marked as executable or not. - -See also [`ispath`](@ref), [`isreadable`](@ref), [`iswriteable`](@ref). -""" -function isexecutable(path::String) - # We use `access()` and `X_OK` to determine if a given path is - # executable by the current user. `X_OK` comes from `unistd.h`. - X_OK = 0x01 - return ccall(:jl_fs_access, Cint, (Cstring, Cint), path, X_OK) == 0 -end -isexecutable(path::AbstractString) = isexecutable(String(path)) - -""" - Sys.isreadable(path::String) - -Return `true` if the access permissions for the given `path` permitted reading by the current user. - -!!! note - This permission may change before the user calls `open`, - so it is recommended to just call `open` alone and handle the error if that fails, - rather than calling `isreadable` first. - -!!! note - Currently this function does not correctly interrogate filesystem - ACLs on Windows, therefore it can return wrong results. - -!!! compat "Julia 1.11" - This function requires at least Julia 1.11. - -See also [`ispath`](@ref), [`isexecutable`](@ref), [`iswriteable`](@ref). -""" -function isreadable(path::String) - # We use `access()` and `R_OK` to determine if a given path is - # readable by the current user. `R_OK` comes from `unistd.h`. - R_OK = 0x04 - return ccall(:jl_fs_access, Cint, (Cstring, Cint), path, R_OK) == 0 -end -isreadable(path::AbstractString) = isreadable(String(path)) - -""" - Sys.iswriteable(path::String) - -Return `true` if the access permissions for the given `path` permitted writing by the current user. - -!!! note - This permission may change before the user calls `open`, - so it is recommended to just call `open` alone and handle the error if that fails, - rather than calling `iswriteable` first. - -!!! note - Currently this function does not correctly interrogate filesystem - ACLs on Windows, therefore it can return wrong results. - -!!! compat "Julia 1.11" - This function requires at least Julia 1.11. - -See also [`ispath`](@ref), [`isexecutable`](@ref), [`isreadable`](@ref). -""" -function iswriteable(path::String) - # We use `access()` and `W_OK` to determine if a given path is - # writeable by the current user. `W_OK` comes from `unistd.h`. - W_OK = 0x02 - return ccall(:jl_fs_access, Cint, (Cstring, Cint), path, W_OK) == 0 -end -iswriteable(path::AbstractString) = iswriteable(String(path)) +const isexecutable = Base.isexecutable +const isreadable = Base.isreadable +const iswritable = Base.iswritable """ Sys.which(program_name::String) diff --git a/doc/src/base/base.md b/doc/src/base/base.md index 556d51d3af0c1..739ee97d1dd43 100644 --- a/doc/src/base/base.md +++ b/doc/src/base/base.md @@ -375,7 +375,7 @@ Base.Sys.isjsvm Base.Sys.loadavg Base.Sys.isexecutable Base.Sys.isreadable -Base.Sys.iswriteable +Base.Sys.iswritable Base.Sys.username Base.@static ``` diff --git a/doc/src/base/io-network.md b/doc/src/base/io-network.md index f99f592e0fb5c..123d494c396a0 100644 --- a/doc/src/base/io-network.md +++ b/doc/src/base/io-network.md @@ -39,6 +39,7 @@ Base.eof Base.isreadonly Base.iswritable Base.isreadable +Base.isexecutable Base.isopen Base.fd Base.redirect_stdio diff --git a/test/file.jl b/test/file.jl index a5ab29a3bf8bc..8c5c3ebc4b74b 100644 --- a/test/file.jl +++ b/test/file.jl @@ -1671,7 +1671,7 @@ else ) end -@testset "chmod/isexecutable/isreadable/iswriteable" begin +@testset "chmod/isexecutable/isreadable/iswritable" begin mktempdir() do dir subdir = joinpath(dir, "subdir") fpath = joinpath(dir, "subdir", "foo") @@ -1688,19 +1688,19 @@ end chmod(fpath, 0o644) @test !Sys.isexecutable(fpath) @test Sys.isreadable(fpath) - @test Sys.iswriteable(fpath) skip=Sys.iswindows() + @test Sys.iswritable(fpath) skip=Sys.iswindows() chmod(fpath, 0o755) @test Sys.isexecutable(fpath) @test Sys.isreadable(fpath) - @test Sys.iswriteable(fpath) skip=Sys.iswindows() + @test Sys.iswritable(fpath) skip=Sys.iswindows() chmod(fpath, 0o444) @test !Sys.isexecutable(fpath) @test Sys.isreadable(fpath) - @test !Sys.iswriteable(fpath) + @test !Sys.iswritable(fpath) chmod(fpath, 0o244) @test !Sys.isexecutable(fpath) @test !Sys.isreadable(fpath) skip=Sys.iswindows() - @test Sys.iswriteable(fpath) skip=Sys.iswindows() + @test Sys.iswritable(fpath) skip=Sys.iswindows() # Ensure that, on Windows, where inheritance is default, # chmod still behaves as we expect. @@ -1708,7 +1708,7 @@ end chmod(subdir, 0o666) @test !Sys.isexecutable(fpath) @test Sys.isreadable(fpath) - @test_skip Sys.iswriteable(fpath) + @test_skip Sys.iswritable(fpath) end # Reset permissions to all at the end, so it can be deleted properly.