From 788acce5619ec8b9a5ee946ebacf00c4afa9133a Mon Sep 17 00:00:00 2001 From: Stefan Karpinski Date: Tue, 27 Aug 2019 13:02:44 -0400 Subject: [PATCH 1/3] tempname(parent): add optional `parent` argument to `tempname` --- NEWS.md | 1 + base/file.jl | 29 +++++++++++++++++++++-------- test/file.jl | 12 ++++++++++++ 3 files changed, 34 insertions(+), 8 deletions(-) diff --git a/NEWS.md b/NEWS.md index 7f55e296898b3..42d4ace8b5e3c 100644 --- a/NEWS.md +++ b/NEWS.md @@ -21,6 +21,7 @@ New library functions --------------------- * The `splitpath` function now accepts any `AbstractString` whereas previously it only accepted paths of type `String` ([#33012]). +* The `tempname` function now takes an optional `parent::AbstractString` argument to give it a directory in which to attempt to produce a temporary path name ([#33090]). Standard library changes diff --git a/base/file.jl b/base/file.jl index b02df0fa7b530..567cb1f2ecfb2 100644 --- a/base/file.jl +++ b/base/file.jl @@ -500,8 +500,8 @@ function mktemp(parent::AbstractString=tempdir(); cleanup::Bool=true) return (filename, Base.open(filename, "r+")) end -function tempname() - parent = tempdir() +function tempname(parent::AbstractString=tempdir()) + isdir(parent) || throw(ArgumentError("$(repr(parent)) is not a directory")) seed::UInt32 = rand(UInt32) while true if (seed & typemax(UInt16)) == 0 @@ -516,10 +516,11 @@ function tempname() end else # !windows + # Obtain a temporary filename. -function tempname() - d = tempdir() # tempnam ignores TMPDIR on darwin - p = ccall(:tempnam, Cstring, (Cstring, Cstring), d, temp_prefix) +function tempname(parent::AbstractString=tempdir()) + isdir(parent) || throw(ArgumentError("$(repr(parent)) is not a directory")) + p = ccall(:tempnam, Cstring, (Cstring, Cstring), parent, temp_prefix) systemerror(:tempnam, p == C_NULL) s = unsafe_string(p) Libc.free(p) @@ -540,16 +541,28 @@ end # os-test """ - tempname() + tempname(parent=tempdir()) -> String Generate a temporary file path. This function only returns a path; no file is -created. The path is likely to be unique, but this cannot be guaranteed. +created. The path is likely to be unique, but this cannot be guaranteed due to +the very remote posibility of two simultaneous calls to `tempname` generating +the same file name. The name is guaranteed to differ from all files already +existing at the time of the call to `tempname`. + +When called with no arguments, the temporary name will be an absolute path to a +temporary name in the system temporary directory as given by `tempdir()`. If a +`parent` directory argument is given, the temporary path will be in that +directory instead. + +!!! compat "Julia 1.4" + The `parent` argument was added in 1.4. !!! warning This can lead to security holes if another process obtains the same file name and creates the file before you are able to. Open the file with - `JL_O_EXCL` if this is a concern. Using [`mktemp()`](@ref) is also recommended instead. + `JL_O_EXCL` if this is a concern. Using [`mktemp()`](@ref) is also + recommended instead. """ tempname() diff --git a/test/file.jl b/test/file.jl index 1c8a9f53509d4..04130c4c307a6 100644 --- a/test/file.jl +++ b/test/file.jl @@ -48,6 +48,18 @@ if !Sys.iswindows() cd(pwd_) end +using Random + +@testset "tempname with parent" begin + t = tempname() + @test dirname(t) == tempdir() + mktempdir() do d + t = tempname(d) + @test dirname(t) == d + end + @test_throws ArgumentError tempname(randstring()) +end + child_eval(code::String) = eval(Meta.parse(readchomp(`$(Base.julia_cmd()) -E $code`))) @testset "mktemp/dir basic cleanup" begin From 625fab37da91e43f32372ee28c9b18d1ae832e15 Mon Sep 17 00:00:00 2001 From: Stefan Karpinski Date: Tue, 27 Aug 2019 13:03:25 -0400 Subject: [PATCH 2/3] tempname(cleanup=true|false): add `cleanup` keyword to `tempname` --- NEWS.md | 1 + base/file.jl | 17 +++++++++++++---- test/file.jl | 8 ++++++++ 3 files changed, 22 insertions(+), 4 deletions(-) diff --git a/NEWS.md b/NEWS.md index 42d4ace8b5e3c..04ea0cc71124b 100644 --- a/NEWS.md +++ b/NEWS.md @@ -22,6 +22,7 @@ New library functions * The `splitpath` function now accepts any `AbstractString` whereas previously it only accepted paths of type `String` ([#33012]). * The `tempname` function now takes an optional `parent::AbstractString` argument to give it a directory in which to attempt to produce a temporary path name ([#33090]). +* The `tempname` function now takes a `cleanup::Bool` keyword argument defaulting to `false`; when set to `true` the process will try to ensure that any file or directory at the path returned by `tempname` is deleted upon process exit ([#33090]). Standard library changes diff --git a/base/file.jl b/base/file.jl index 567cb1f2ecfb2..72b9bb1bd175e 100644 --- a/base/file.jl +++ b/base/file.jl @@ -500,7 +500,7 @@ function mktemp(parent::AbstractString=tempdir(); cleanup::Bool=true) return (filename, Base.open(filename, "r+")) end -function tempname(parent::AbstractString=tempdir()) +function tempname(parent::AbstractString=tempdir(); cleanup::Bool=false) isdir(parent) || throw(ArgumentError("$(repr(parent)) is not a directory")) seed::UInt32 = rand(UInt32) while true @@ -509,6 +509,7 @@ function tempname(parent::AbstractString=tempdir()) end filename = _win_tempname(parent, seed) if !ispath(filename) + cleanup && temp_cleanup_later(filename) return filename end seed += 1 @@ -518,12 +519,13 @@ end else # !windows # Obtain a temporary filename. -function tempname(parent::AbstractString=tempdir()) +function tempname(parent::AbstractString=tempdir(); cleanup::Bool=false) isdir(parent) || throw(ArgumentError("$(repr(parent)) is not a directory")) p = ccall(:tempnam, Cstring, (Cstring, Cstring), parent, temp_prefix) systemerror(:tempnam, p == C_NULL) s = unsafe_string(p) Libc.free(p) + cleanup && temp_cleanup_later(s) return s end @@ -541,7 +543,7 @@ end # os-test """ - tempname(parent=tempdir()) -> String + tempname(parent=tempdir(); cleanup=false) -> String Generate a temporary file path. This function only returns a path; no file is created. The path is likely to be unique, but this cannot be guaranteed due to @@ -554,8 +556,15 @@ temporary name in the system temporary directory as given by `tempdir()`. If a `parent` directory argument is given, the temporary path will be in that directory instead. +The `cleanup` option controls whether the process attempts to delete the +returned path automatically when the process exits. Note that the `tempname` +function does not create any file or directory at the returned location, so +there is nothing to cleanup unless you create a file or directory there. If +you do and `clean` is `true` it will be deleted upon process termination. + !!! compat "Julia 1.4" - The `parent` argument was added in 1.4. + The `parent` and `cleanup` arguments were added in 1.4. Prior to Julia 1.4 + the path `tempname` would never be cleaned up at process termination. !!! warning diff --git a/test/file.jl b/test/file.jl index 04130c4c307a6..7a1030705c92a 100644 --- a/test/file.jl +++ b/test/file.jl @@ -79,6 +79,14 @@ child_eval(code::String) = eval(Meta.parse(readchomp(`$(Base.julia_cmd()) -E $co # mktempdir with cleanup t = child_eval("t = mktempdir(); touch(joinpath(t, \"file.txt\")); t") @test !ispath(t) + # tempname without cleanup + t = child_eval("t = tempname(); touch(t); t") + @test isfile(t) + rm(t, force=true) + @test !ispath(t) + # tempname with cleanup + t = child_eval("t = tempname(cleanup=true); touch(t); t") + @test !ispath(t) end import Base.Filesystem: TEMP_CLEANUP_MIN, TEMP_CLEANUP_MAX, TEMP_CLEANUP From 75cb00604e149ad3e963958189988586bf7a2d32 Mon Sep 17 00:00:00 2001 From: Stefan Karpinski Date: Tue, 27 Aug 2019 13:07:08 -0400 Subject: [PATCH 3/3] tempname: change the default of `cleanup` from `false` to `true` --- NEWS.md | 2 +- base/file.jl | 6 +++--- test/file.jl | 4 ++-- 3 files changed, 6 insertions(+), 6 deletions(-) diff --git a/NEWS.md b/NEWS.md index 04ea0cc71124b..fbe5eadd9418f 100644 --- a/NEWS.md +++ b/NEWS.md @@ -22,7 +22,7 @@ New library functions * The `splitpath` function now accepts any `AbstractString` whereas previously it only accepted paths of type `String` ([#33012]). * The `tempname` function now takes an optional `parent::AbstractString` argument to give it a directory in which to attempt to produce a temporary path name ([#33090]). -* The `tempname` function now takes a `cleanup::Bool` keyword argument defaulting to `false`; when set to `true` the process will try to ensure that any file or directory at the path returned by `tempname` is deleted upon process exit ([#33090]). +* The `tempname` function now takes a `cleanup::Bool` keyword argument defaulting to `true`, which causes the process to try to ensure that any file or directory at the path returned by `tempname` is deleted upon process exit ([#33090]). Standard library changes diff --git a/base/file.jl b/base/file.jl index 72b9bb1bd175e..d57024a5cf782 100644 --- a/base/file.jl +++ b/base/file.jl @@ -500,7 +500,7 @@ function mktemp(parent::AbstractString=tempdir(); cleanup::Bool=true) return (filename, Base.open(filename, "r+")) end -function tempname(parent::AbstractString=tempdir(); cleanup::Bool=false) +function tempname(parent::AbstractString=tempdir(); cleanup::Bool=true) isdir(parent) || throw(ArgumentError("$(repr(parent)) is not a directory")) seed::UInt32 = rand(UInt32) while true @@ -519,7 +519,7 @@ end else # !windows # Obtain a temporary filename. -function tempname(parent::AbstractString=tempdir(); cleanup::Bool=false) +function tempname(parent::AbstractString=tempdir(); cleanup::Bool=true) isdir(parent) || throw(ArgumentError("$(repr(parent)) is not a directory")) p = ccall(:tempnam, Cstring, (Cstring, Cstring), parent, temp_prefix) systemerror(:tempnam, p == C_NULL) @@ -543,7 +543,7 @@ end # os-test """ - tempname(parent=tempdir(); cleanup=false) -> String + tempname(parent=tempdir(); cleanup=true) -> String Generate a temporary file path. This function only returns a path; no file is created. The path is likely to be unique, but this cannot be guaranteed due to diff --git a/test/file.jl b/test/file.jl index 7a1030705c92a..fe93448adabbe 100644 --- a/test/file.jl +++ b/test/file.jl @@ -80,12 +80,12 @@ child_eval(code::String) = eval(Meta.parse(readchomp(`$(Base.julia_cmd()) -E $co t = child_eval("t = mktempdir(); touch(joinpath(t, \"file.txt\")); t") @test !ispath(t) # tempname without cleanup - t = child_eval("t = tempname(); touch(t); t") + t = child_eval("t = tempname(cleanup=false); touch(t); t") @test isfile(t) rm(t, force=true) @test !ispath(t) # tempname with cleanup - t = child_eval("t = tempname(cleanup=true); touch(t); t") + t = child_eval("t = tempname(); touch(t); t") @test !ispath(t) end