From 13302e6dd94b188d6a09ec35f7f0291e77ee3686 Mon Sep 17 00:00:00 2001 From: peter1000 Date: Mon, 20 Apr 2015 21:26:25 -0300 Subject: [PATCH] adds_function_relpath, docs, tests This adds function relpath which has similar functionalities as python's os.path.relpath Return a relative filepath to path either from the current directory or from an optional start directory. This is a path computation: the filesystem is not accessed to confirm the existence or nature of path or start. --- NEWS.md | 4 +++ base/exports.jl | 1 + base/path.jl | 29 ++++++++++++++++ doc/helpdb.jl | 9 +++++ doc/stdlib/file.rst | 7 ++++ test/path.jl | 82 +++++++++++++++++++++++++++++++++++++++++++++ 6 files changed, 132 insertions(+) diff --git a/NEWS.md b/NEWS.md index 88f63f835df70..3c10d3663c554 100644 --- a/NEWS.md +++ b/NEWS.md @@ -274,6 +274,9 @@ Library improvements * New `withenv(var=>val, ...) do ... end` function to temporarily modify environment variables ([#10914]). + * New function `relpath` returns a relative filepath to path either from the current + directory or from an optional start directory ([#10893]). + Deprecated or removed --------------------- @@ -1375,4 +1378,5 @@ Too numerous to mention. [#10844]: https://github.com/JuliaLang/julia/issues/10844 [#10870]: https://github.com/JuliaLang/julia/issues/10870 [#10885]: https://github.com/JuliaLang/julia/issues/10885 +[#10893]: https://github.com/JuliaLang/julia/pull/10893 [#10914]: https://github.com/JuliaLang/julia/issues/10914 diff --git a/base/exports.jl b/base/exports.jl index 5fc6e049bff16..98d358a445aec 100644 --- a/base/exports.jl +++ b/base/exports.jl @@ -1242,6 +1242,7 @@ export joinpath, normpath, realpath, + relpath, splitdir, splitdrive, splitext, diff --git a/base/path.jl b/base/path.jl index c5984e04e0e0f..2c015041a6276 100644 --- a/base/path.jl +++ b/base/path.jl @@ -141,3 +141,32 @@ end if c == '/' return homedir()*path[i:end] end throw(ArgumentError("~user tilde expansion not yet implemented")) end + +function relpath(path::AbstractString, startpath::AbstractString = ".") + isempty(path) && throw(ArgumentError("`path` must be specified")) + isempty(startpath) && throw(ArgumentError("`startpath` must be specified")) + curdir = "." + pardir = ".." + path == startpath && return curdir + path_arr = split(abspath(path), path_separator_re) + start_arr = split(abspath(startpath), path_separator_re) + i = 0 + while i < min(length(path_arr), length(start_arr)) + i += 1 + if path_arr[i] != start_arr[i] + i -= 1 + break + end + end + pathpart = join(path_arr[i+1:findlast(x -> !isempty(x), path_arr)], path_separator) + prefix_num = findlast(x -> !isempty(x), start_arr) - i - 1 + if prefix_num >= 0 + prefix = pardir * path_separator + relpath_ = isempty(pathpart) ? + (prefix^prefix_num) * pardir : + (prefix^prefix_num) * pardir * path_separator * pathpart + else + relpath_ = pathpart + end + return isempty(relpath_) ? curdir : relpath_ +end diff --git a/doc/helpdb.jl b/doc/helpdb.jl index d37a47c7711ab..145dca41be221 100644 --- a/doc/helpdb.jl +++ b/doc/helpdb.jl @@ -5354,6 +5354,15 @@ Millisecond(v) "), +("Base","relpath","relpath(path::AbstractString, startpath::AbstractString = ".") -> AbstractString + + Return a relative filepath to path either from the current directory or from an optional + start directory. + This is a path computation: the filesystem is not accessed to confirm the existence or + nature of path or startpath. + +"), + ("Base","expanduser","expanduser(path::AbstractString) -> AbstractString On Unix systems, replace a tilde character at the start of a path diff --git a/doc/stdlib/file.rst b/doc/stdlib/file.rst index 067729f0f936b..f3b5a86f32b84 100644 --- a/doc/stdlib/file.rst +++ b/doc/stdlib/file.rst @@ -254,6 +254,13 @@ Canonicalize a path by expanding symbolic links and removing "." and ".." entries. +.. function:: relpath(path::AbstractString, startpath::AbstractString = ".") -> AbstractString + + Return a relative filepath to path either from the current directory or from an optional + start directory. + This is a path computation: the filesystem is not accessed to confirm the existence or + nature of path or startpath. + .. function:: expanduser(path::AbstractString) -> AbstractString On Unix systems, replace a tilde character at the start of a path with the diff --git a/test/path.jl b/test/path.jl index 036d7d1a7eda2..820701c0d803b 100644 --- a/test/path.jl +++ b/test/path.jl @@ -3,3 +3,85 @@ @unix_only @test isabspath("/") == true @test isabspath("~") == false @unix_only @test isabspath(expanduser("~")) == true + +############################################ +# This section tests relpath computation. # +########################################### +function test_relpath() + sep = Base.path_separator + filepaths = [ + "$(sep)home$(sep)user$(sep).julia$(sep)Test1$(sep)docs$(sep)api$(sep)Test1.md", + "$(sep)home$(sep)user$(sep).julia$(sep)Test1$(sep)docs$(sep)api$(sep)lib$(sep)file1.md", + "$(sep)home$(sep)user$(sep).julia$(sep)测试2$(sep)docs$(sep)api$(sep)测试2.md", + "$(sep)home$(sep)user$(sep)dir_withendsep$(sep)", + "$(sep)home$(sep)dir2_withendsep$(sep)", + "$(sep)home$(sep)test.md", + "$(sep)home", + # Special cases + "$(sep)", + "$(sep)home$(sep)$(sep)$(sep)" + ] + startpaths = [ + "$(sep)home$(sep)user$(sep).julia$(sep)Test1$(sep)docs$(sep)api$(sep)genindex.md", + "$(sep)multi_docs$(sep)genindex.md", + "$(sep)home$(sep)user$(sep)dir_withendsep$(sep)", + "$(sep)home$(sep)dir2_withendsep$(sep)", + "$(sep)home$(sep)test.md", + "$(sep)home", + # Special cases + "$(sep)", + "$(sep)home$(sep)$(sep)$(sep)" + ] + relpath_expected_results = [ + "..$(sep)Test1.md", + "..$(sep)..$(sep)home$(sep)user$(sep).julia$(sep)Test1$(sep)docs$(sep)api$(sep)Test1.md", + "..$(sep).julia$(sep)Test1$(sep)docs$(sep)api$(sep)Test1.md", + "..$(sep)user$(sep).julia$(sep)Test1$(sep)docs$(sep)api$(sep)Test1.md", + "..$(sep)user$(sep).julia$(sep)Test1$(sep)docs$(sep)api$(sep)Test1.md", + "user$(sep).julia$(sep)Test1$(sep)docs$(sep)api$(sep)Test1.md", + "home$(sep)user$(sep).julia$(sep)Test1$(sep)docs$(sep)api$(sep)Test1.md", + "user$(sep).julia$(sep)Test1$(sep)docs$(sep)api$(sep)Test1.md", + "..$(sep)lib$(sep)file1.md", + "..$(sep)..$(sep)home$(sep)user$(sep).julia$(sep)Test1$(sep)docs$(sep)api$(sep)lib$(sep)file1.md", + "..$(sep).julia$(sep)Test1$(sep)docs$(sep)api$(sep)lib$(sep)file1.md", + "..$(sep)user$(sep).julia$(sep)Test1$(sep)docs$(sep)api$(sep)lib$(sep)file1.md", + "..$(sep)user$(sep).julia$(sep)Test1$(sep)docs$(sep)api$(sep)lib$(sep)file1.md", + "user$(sep).julia$(sep)Test1$(sep)docs$(sep)api$(sep)lib$(sep)file1.md", + "home$(sep)user$(sep).julia$(sep)Test1$(sep)docs$(sep)api$(sep)lib$(sep)file1.md", + "user$(sep).julia$(sep)Test1$(sep)docs$(sep)api$(sep)lib$(sep)file1.md", + "..$(sep)..$(sep)..$(sep)..$(sep)测试2$(sep)docs$(sep)api$(sep)测试2.md", + "..$(sep)..$(sep)home$(sep)user$(sep).julia$(sep)测试2$(sep)docs$(sep)api$(sep)测试2.md", + "..$(sep).julia$(sep)测试2$(sep)docs$(sep)api$(sep)测试2.md", + "..$(sep)user$(sep).julia$(sep)测试2$(sep)docs$(sep)api$(sep)测试2.md", + "..$(sep)user$(sep).julia$(sep)测试2$(sep)docs$(sep)api$(sep)测试2.md", + "user$(sep).julia$(sep)测试2$(sep)docs$(sep)api$(sep)测试2.md", + "home$(sep)user$(sep).julia$(sep)测试2$(sep)docs$(sep)api$(sep)测试2.md", + "user$(sep).julia$(sep)测试2$(sep)docs$(sep)api$(sep)测试2.md", + "..$(sep)..$(sep)..$(sep)..$(sep)..$(sep)dir_withendsep", + "..$(sep)..$(sep)home$(sep)user$(sep)dir_withendsep",".","..$(sep)user$(sep)dir_withendsep", + "..$(sep)user$(sep)dir_withendsep","user$(sep)dir_withendsep", + "home$(sep)user$(sep)dir_withendsep","user$(sep)dir_withendsep", + "..$(sep)..$(sep)..$(sep)..$(sep)..$(sep)..$(sep)dir2_withendsep", + "..$(sep)..$(sep)home$(sep)dir2_withendsep","..$(sep)..$(sep)dir2_withendsep",".", + "..$(sep)dir2_withendsep","dir2_withendsep","home$(sep)dir2_withendsep","dir2_withendsep", + "..$(sep)..$(sep)..$(sep)..$(sep)..$(sep)..$(sep)test.md","..$(sep)..$(sep)home$(sep)test.md", + "..$(sep)..$(sep)test.md","..$(sep)test.md",".","test.md","home$(sep)test.md","test.md", + "..$(sep)..$(sep)..$(sep)..$(sep)..$(sep)..","..$(sep)..$(sep)home","..$(sep)..", + "..","..",".","home",".","..$(sep)..$(sep)..$(sep)..$(sep)..$(sep)..$(sep)..","..$(sep)..", + "..$(sep)..$(sep)..","..$(sep)..","..$(sep)..","..",".","..", + "..$(sep)..$(sep)..$(sep)..$(sep)..$(sep)..","..$(sep)..$(sep)home","..$(sep)..", + "..","..",".","home","." + ] + idx = 0 + for filep in filepaths + for startp in startpaths + res = relpath(filep, startp) + idx += 1 + @test res == relpath_expected_results[idx] + end + end + # Additional cases + @test_throws ArgumentError relpath("$(sep)home$(sep)user$(sep)dir_withendsep$(sep)", "") + @test_throws ArgumentError relpath("", "$(sep)home$(sep)user$(sep)dir_withendsep$(sep)") +end +test_relpath()