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

Make file operations return the path they created #27071

Merged
merged 6 commits into from
May 18, 2018
Merged
Show file tree
Hide file tree
Changes from 5 commits
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
3 changes: 3 additions & 0 deletions NEWS.md
Original file line number Diff line number Diff line change
Expand Up @@ -469,6 +469,9 @@ This section lists changes that do not have deprecation warnings.
* `widen` on 8- and 16-bit integer types now widens to the platform word size (`Int`)
instead of to a 32-bit type ([#26859]).

* `mv`,`cp`, `touch`, `mkdir`, `mkpath` now return the path that was created/modified
rather than `nothing` ([#27071]).

Library improvements
--------------------

Expand Down
20 changes: 15 additions & 5 deletions base/file.jl
Original file line number Diff line number Diff line change
Expand Up @@ -98,6 +98,7 @@ modified by the current file creation mask. This function never creates more tha
directory. If the directory already exists, or some intermediate directories do not exist,
this function throws an error. See [`mkpath`](@ref) for a function which creates all
required intermediate directories.
Returns `path`
"""
function mkdir(path::AbstractString; mode::Integer = 0o777)
@static if Sys.iswindows()
Expand All @@ -106,13 +107,15 @@ function mkdir(path::AbstractString; mode::Integer = 0o777)
ret = ccall(:mkdir, Int32, (Cstring, UInt32), path, checkmode(mode))
end
systemerror(:mkdir, ret != 0; extrainfo=path)
path
end

"""
mkpath(path::AbstractString; mode::Unsigned = 0o777)

Create all directories in the given `path`, with permissions `mode`. `mode` defaults to
`0o777`, modified by the current file creation mask.
Returns `path`.
"""
function mkpath(path::AbstractString; mode::Integer = 0o777)
isdirpath(path) && (path = dirname(path))
Expand All @@ -124,12 +127,11 @@ function mkpath(path::AbstractString; mode::Integer = 0o777)
# If there is a problem with making the directory, but the directory
# does in fact exist, then ignore the error. Else re-throw it.
catch err
if isa(err, SystemError) && isdir(path)
return
else
if !isa(err, SystemError) || !isdir(path)
rethrow()
end
end
path
end

"""
Expand Down Expand Up @@ -228,6 +230,7 @@ Copy the file, link, or directory from `src` to `dest`.
If `follow_symlinks=false`, and `src` is a symbolic link, `dst` will be created as a
symbolic link. If `follow_symlinks=true` and `src` is a symbolic link, `dst` will be a copy
of the file or directory `src` refers to.
Returns `dst`.
"""
function cp(src::AbstractString, dst::AbstractString; force::Bool=false,
follow_symlinks::Bool=false,
Expand All @@ -246,13 +249,15 @@ function cp(src::AbstractString, dst::AbstractString; force::Bool=false,
else
sendfile(src, dst)
end
dst
end

"""
mv(src::AbstractString, dst::AbstractString; force::Bool=false)

Move the file, link, or directory from `src` to `dst`.
`force=true` will first remove an existing `dst`.
Returns `dst`.
"""
function mv(src::AbstractString, dst::AbstractString; force::Bool=false,
remove_destination::Union{Bool,Nothing}=nothing)
Expand All @@ -264,12 +269,14 @@ function mv(src::AbstractString, dst::AbstractString; force::Bool=false,
end
checkfor_mv_cp_cptree(src, dst, "moving"; force=force)
rename(src, dst)
dst
end

"""
touch(path::AbstractString)

Update the last-modified timestamp on a file to the current time.
Returns `path`.
"""
function touch(path::AbstractString)
f = open(path, JL_O_WRONLY | JL_O_CREAT, 0o0666)
Expand All @@ -279,6 +286,7 @@ function touch(path::AbstractString)
finally
close(f)
end
path
end

if Sys.iswindows()
Expand Down Expand Up @@ -650,6 +658,7 @@ end
Change the permissions mode of `path` to `mode`. Only integer `mode`s (e.g. `0o777`) are
currently supported. If `recursive=true` and the path is a directory all permissions in
that directory will be recursively changed.
Returns `path`.
Copy link
Member

Choose a reason for hiding this comment

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

Returns -> Return I think.

Copy link
Contributor Author

Choose a reason for hiding this comment

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

I think Returns, as in "This function returns path"

Overly complicated explanation for why:
It is a sentence incorporating ellipsis. which is short for "This function returns path"
Returns is the verb in 3rd person present tense (I had to look that up).

Copy link
Member

Choose a reason for hiding this comment

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

We usually use imperative form though, from point 2 in the manual (https://docs.julialang.org/en/latest/manual/documentation/):

The one-line sentence should use the imperative form ("Do this", "Return that") instead of the third person (do not write "Returns the length...") when documenting functions.

Copy link
Contributor Author

@oxinabox oxinabox May 14, 2018

Choose a reason for hiding this comment

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

Fair enough, amazing and excellent that the manual is that specific

Copy link
Member

Choose a reason for hiding this comment

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

However, from searching a bit in the doc strings, we are very inconsistent regarding this.

Copy link
Contributor

Choose a reason for hiding this comment

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

The imperative mode guideline was introduced in #15136, among several other guidelines, by @nalimilan. However, I can't seem to find discussion of that particular aspect in the PR. Was it discussed elsewhere, e.g. on discourse or slack?

(FWIW, I do agree with @KristofferC that indicative mode sounds more natural, as neither the author nor the reader of the documentation will be the actor performing the returning action... but I may be missing something.)

Copy link
Member

Choose a reason for hiding this comment

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

I don't remember where that rule comes from, but I don't think I invented it. It's a know problem that our docs are inconsistent, and I don't really care about which convention is chosen as long as one exists.

Copy link
Contributor

@waldyrious waldyrious May 14, 2018

Choose a reason for hiding this comment

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

I do know of that convention/recommendation for git commits, but not for function documentation. I do think it makes more sense in the former context than the latter, but don't meant to discuss this here. Should I open an issue for that, or is everyone else ok with the current convention?

Copy link
Member

Choose a reason for hiding this comment

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

Note that the cited rule applies to the one-line sentence in the following sense:

Include a single one-line sentence describing what the function does or what the object represents after the simplified signature block. If needed, provide more details in a second paragraph, after a blank line

So no need to discuss the rule here, because it doesn't apply.

"""
function chmod(path::AbstractString, mode::Integer; recursive::Bool=false)
err = ccall(:jl_fs_chmod, Int32, (Cstring, Cint), path, mode)
Expand All @@ -661,17 +670,18 @@ function chmod(path::AbstractString, mode::Integer; recursive::Bool=false)
end
end
end
nothing
path
end

"""
chown(path::AbstractString, owner::Integer, group::Integer=-1)

Change the owner and/or group of `path` to `owner` and/or `group`. If the value entered for `owner` or `group`
is `-1` the corresponding ID will not change. Only integer `owner`s and `group`s are currently supported.
Returns `path`
"""
function chown(path::AbstractString, owner::Integer, group::Integer=-1)
err = ccall(:jl_fs_chown, Int32, (Cstring, Cint, Cint), path, owner, group)
uv_error("chown",err)
nothing
Copy link
Member

Choose a reason for hiding this comment

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

Maybe chmod and chown should return the path too?

Copy link
Contributor Author

Choose a reason for hiding this comment

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

done

path
end
26 changes: 23 additions & 3 deletions test/file.jl
Original file line number Diff line number Diff line change
Expand Up @@ -62,7 +62,7 @@ end

@test filemode(file) & 0o444 > 0 # readable
@test filemode(file) & 0o222 > 0 # writable
chmod(file, filemode(file) & 0o7555)
@test chmod(file, filemode(file) & 0o7555) == file
@test filemode(file) & 0o222 == 0
chmod(file, filemode(file) | 0o222)
@test filemode(file) & 0o111 == 0
Expand Down Expand Up @@ -171,7 +171,7 @@ if !Sys.iswindows()
chown(file, 0, -2) # Change the file group to nogroup (and owner back to root)
@test stat(file).gid !=0
@test stat(file).uid ==0
chown(file, -1, 0)
@test chown(file, -1, 0) == file
@test stat(file).gid ==0
@test stat(file).uid ==0
else
Expand All @@ -180,7 +180,7 @@ if !Sys.iswindows()
end
else
# test that chown doesn't cause any errors for Windows
@test chown(file, -2, -2) === nothing
@test chown(file, -2, -2) == file
end

##############
Expand Down Expand Up @@ -978,6 +978,26 @@ rm(dir)
@test !ispath(file)
@test !ispath(dir)



##################
# Return values of mkpath, mkdir, cp, mv and touch
####################
mktempdir() do dir
name1 = joinpath(dir, "apples")
name2 = joinpath(dir, "bannanas")
@test touch(name1)==name1
@test mv(name1, name2) == name2
@test cp(name2, name1) == name1
namedir = joinpath(dir, "chalk")
namepath = joinpath(dir, "chalk","cheese","fresh")
@test mkdir(namedir) == namedir
@test mkpath(namepath) == namepath
end




# issue #9687
let n = tempname()
w = open(n, "a")
Expand Down