Julia 1.11:
cd(mktempdir())
mkpath("uwe/")
Output: "uwe"
Julia 1.12-rc1:
cd(mktempdir())
mkpath("uwe/")
Output: "uwe/"
This difference causes my unit tests to fail.
Is this a bug, or just undefined behaviour?
This is on Ubuntu Linux.
If you care about your mental sanity, you should really not rely on the trailing path separator being there, you should always get rid of it before doing anything else with that path. See [Breaking] Make `basename` and `dirname` ignore a trailing path separator ยท Issue #43137 ยท JuliaLang/julia ยท GitHub as an example where the trailing separator causes different behaviour.
2 Likes
In principle, I agree. But I still do not know if I should report that as bug in the Julia repo.
That seems to be a deliberate change:
master
โ ctarn:mkpath
opened 12:34PM - 20 Jun 24 UTC
fix #54826
related performance test code:
```julia
using BenchmarkTools
usโฆ ing Base: IOError
function checkmode(mode::Integer)
if !(0 <= mode <= 511)
throw(ArgumentError("Mode must be between 0 and 511 = 0o777"))
end
mode
end
function mkpath(path::AbstractString; mode::Integer = 0o777)
dir = dirname(path)
# stop recursion for `""`, `"/"`, or existed dir
(path == dir || isdir(path)) && return path
mkpath(dir, mode = checkmode(mode))
try
# cases like `mkpath("x/")` will cause an error if `isdir(path)` is skipped
# the error will not be rethrowed, but it may be slower, and thus we avoid it in advance
isdir(path) || mkdir(path, mode = mode)
catch err
# If there is a problem with making the directory, but the directory
# does in fact exist, then ignore the error. Else re-throw it.
if !isa(err, IOError) || !isdir(path)
rethrow()
end
end
return path
end
function mkpath_2(path::AbstractString; mode::Integer = 0o777)
dir = dirname(path)
# stop recursion for `""` and `"/"` or existed dir
(path == dir || isdir(path)) && return path
mkpath_2(dir, mode = checkmode(mode))
try
mkdir(path, mode = mode)
catch err
# If there is a problem with making the directory, but the directory
# does in fact exist, then ignore the error. Else re-throw it.
if !isa(err, IOError) || !isdir(path)
rethrow()
end
end
return path
end
versioninfo()
display(@benchmark begin rm("A", recursive=true, force=true); mkpath("A/B/C/D/") end)
display(@benchmark begin rm("A", recursive=true, force=true); mkpath_2("A/B/C/D/") end)
```
output:
```
Julia Version 1.10.4
Commit 48d4fd48430 (2024-06-04 10:41 UTC)
Build Info:
Official https://julialang.org/ release
Platform Info:
OS: macOS (x86_64-apple-darwin22.4.0)
CPU: 16 ร Intel(R) Core(TM) i9-9900K CPU @ 3.60GHz
WORD_SIZE: 64
LIBM: libopenlibm
LLVM: libLLVM-15.0.7 (ORCJIT, skylake)
Threads: 1 default, 0 interactive, 1 GC (on 16 virtual cores)
Environment:
JULIA_EDITOR = code
JULIA_NUM_THREADS =
BenchmarkTools.Trial: 8683 samples with 1 evaluation.
Range (min โฆ max): 473.972 ฮผs โฆ 18.867 ms โ GC (min โฆ max): 0.00% โฆ 0.00%
Time (median): 519.704 ฮผs โ GC (median): 0.00%
Time (mean ยฑ ฯ): 571.261 ฮผs ยฑ 378.851 ฮผs โ GC (mean ยฑ ฯ): 0.00% ยฑ 0.00%
โโโโโ
โโโโโโโโโโ
โโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโ โ
474 ฮผs Histogram: frequency by time 961 ฮผs <
Memory estimate: 5.98 KiB, allocs estimate: 65.
BenchmarkTools.Trial: 6531 samples with 1 evaluation.
Range (min โฆ max): 588.122 ฮผs โฆ 17.449 ms โ GC (min โฆ max): 0.00% โฆ 0.00%
Time (median): 660.071 ฮผs โ GC (median): 0.00%
Time (mean ยฑ ฯ): 760.333 ฮผs ยฑ 615.759 ฮผs โ GC (mean ยฑ ฯ): 0.00% ยฑ 0.00%
โโโโโโโ โ
โโโโโโโโโโโโโโโโโ
โ
โ
โโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโ
โ
โโ โ
588 ฮผs Histogram: log(frequency) by time 4.2 ms <
Memory estimate: 5.63 KiB, allocs estimate: 72.
```
The linked issue specifically talks about the case of directories ending with path separator. The handling of the trailing path separator is maddening.
1 Like