Skip to content

Commit 5de7d46

Browse files
committed
samepath: functionalize improve path, canonical() strict option
remove java as unstable for Java 8 or 11
1 parent d983fab commit 5de7d46

File tree

7 files changed

+116
-54
lines changed

7 files changed

+116
-54
lines changed

+stdlib/+python/samepath.m

Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,13 @@
1+
function y = samepath(path1, path2)
2+
3+
try
4+
y = py.os.path.samefile(path1, path2);
5+
catch e
6+
if contains(e.message, "FileNotFoundError")
7+
y = false;
8+
else
9+
rethrow(e);
10+
end
11+
end
12+
13+
end

+stdlib/+sys/samepath.m

Lines changed: 35 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,35 @@
1+
function y = samepath(path1, path2)
2+
3+
y = false;
4+
5+
if strlength(path1) == 0 || strlength(path2) == 0
6+
return
7+
end
8+
9+
if ispc()
10+
c1 = stdlib.canonical(path1, true);
11+
c2 = stdlib.canonical(path2, true);
12+
y = strlength(c1) > 0 && strcmp(c1, c2);
13+
return
14+
end
15+
16+
if ismac()
17+
flag = '-f';
18+
else
19+
flag = '-c';
20+
end
21+
22+
cmd = "stat " + flag + " %d:%i " + path1 + " && stat " + flag + " %d:%i " + path2;
23+
24+
[s, m] = system(cmd);
25+
if s ~= 0
26+
warning("samepath(%s, %s) failed: %s", path1, path2, m);
27+
return
28+
end
29+
30+
m = splitlines(m);
31+
assert(length(m) >= 2, "samepath(%s, %s) failed: unexpected output", path1, path2);
32+
33+
y = strcmp(m{1}, m{2});
34+
35+
end

+stdlib/canonical.m

Lines changed: 10 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -8,12 +8,14 @@
88
%
99
%%% Inputs
1010
% * p: path to make canonical
11+
% * strict: if true, only return canonical path if it exists. If false, return normalized path if path does not exist.
1112
%%% Outputs
1213
% * c: canonical path, if determined
1314

14-
function c = canonical(p)
15+
function c = canonical(p, strict)
1516
arguments
1617
p {mustBeTextScalar}
18+
strict logical = false
1719
end
1820

1921
if strempty(p)
@@ -22,11 +24,11 @@
2224
end
2325

2426
if isMATLABReleaseOlderThan('R2024a')
25-
c = acanon(p);
27+
c = acanon(p, strict);
2628
else
2729
pth = matlab.io.internal.filesystem.resolvePath(p);
2830
c = pth.ResolvedPath;
29-
if strempty(c)
31+
if ~strict && strempty(c)
3032
c = stdlib.normalize(p);
3133
end
3234
end
@@ -36,15 +38,17 @@
3638
end
3739

3840

39-
function c = acanon(p)
41+
function c = acanon(p, strict)
4042

41-
if strempty(p), c = ''; return, end
43+
c = "";
44+
45+
if strempty(p), return, end
4246

4347
[s, r] = fileattrib(p);
4448

4549
if s == 1
4650
c = r.Name;
47-
else
51+
elseif ~strict
4852
c = stdlib.normalize(p);
4953
end
5054

+stdlib/samepath.m

Lines changed: 3 additions & 24 deletions
Original file line numberDiff line numberDiff line change
@@ -16,31 +16,10 @@
1616
path2 {mustBeTextScalar}
1717
end
1818

19-
y = stdlib.exists(path1) && stdlib.exists(path2);
20-
21-
if ~y, return; end
22-
23-
if ~ispc() && stdlib.isoctave()
24-
[r1, e1] = stat(path1);
25-
[r2, e2] = stat(path2);
26-
27-
y = e1 == 0 && e2 == 0 && ...
28-
r1.ino == r2.ino && r1.dev == r2.dev;
29-
30-
elseif stdlib.has_dotnet()
31-
separators = [System.IO.Path.DirectorySeparatorChar, System.IO.Path.AltDirectorySeparatorChar];
32-
f1 = System.IO.Path.GetFullPath(path1).TrimEnd(separators);
33-
f2 = System.IO.Path.GetFullPath(path2).TrimEnd(separators);
34-
y = System.String.Equals(f1, f2, System.StringComparison.OrdinalIgnoreCase);
35-
elseif stdlib.java_api() >= 11
36-
% https://docs.oracle.com/en/java/javase/24/docs/api/java.base/java/nio/file/Files.html#isSameFile(java.nio.file.Path,java.nio.file.Path)
37-
% Java 1.8 is buggy in some corner cases, so we require at least 11.
38-
y = java.nio.file.Files.isSameFile(javaPathObject(path1), javaPathObject(path2));
39-
19+
if stdlib.has_python()
20+
y = stdlib.python.samepath(path1, path2);
4021
else
41-
42-
y = strcmp(stdlib.canonical(path1), stdlib.canonical(path2));
43-
22+
y = stdlib.sys.samepath(path1, path2);
4423
end
4524

4625
%!assert(samepath(".", "."))

test/TestFileImpure.m

Lines changed: 9 additions & 24 deletions
Original file line numberDiff line numberDiff line change
@@ -1,17 +1,20 @@
11
classdef TestFileImpure < matlab.unittest.TestCase
22

33
properties(TestParameter)
4-
p_same = {...
5-
{"..", "./.."}, ...
6-
{"..", pwd() + "/.."}, ...
7-
{pwd(), pwd() + "/."}}
8-
94
ph = {{0, '"stdin"'}, {1, '"stdout"'}, {2, '"stderr"'}, {fopen(tempname()), ''}}
105

116
p_file_size = {mfilename("fullpath") + ".m"}
127
end
138

149

10+
methods(TestClassSetup)
11+
function pkg_path(tc)
12+
p = matlab.unittest.fixtures.PathFixture(fileparts(fileparts(mfilename('fullpath'))));
13+
tc.applyFixture(p)
14+
end
15+
end
16+
17+
1518
methods (Test, TestTags="impure")
1619

1720
function test_file_size(tc, p_file_size)
@@ -31,28 +34,10 @@ function test_makedir(tc)
3134
import matlab.unittest.constraints.IsFolder
3235
d = tempname();
3336
stdlib.makedir(d)
34-
tc.assertThat(d, IsFolder)
37+
tc.verifyThat(d, IsFolder)
3538
rmdir(d)
3639
end
3740

38-
%%
39-
function test_samepath(tc, p_same)
40-
tc.verifyTrue(stdlib.samepath(p_same{1}, p_same{2}))
41-
end
42-
43-
function test_samepath_notexist(tc)
44-
tc.verifyFalse(stdlib.samepath("", ""))
45-
t = tempname();
46-
tc.verifyFalse(stdlib.samepath(t, t))
47-
end
48-
49-
50-
function test_get_pid(tc)
51-
pid = stdlib.get_pid();
52-
tc.verifyGreaterThan(pid, 0)
53-
tc.verifyClass(pid, 'uint64')
54-
end
55-
5641

5742
function test_handle2filename(tc, ph)
5843
tc.verifyEqual(stdlib.handle2filename(ph{1}), ph{2})

test/TestFileSame.m

Lines changed: 37 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,37 @@
1+
classdef TestFileSame < matlab.unittest.TestCase
2+
3+
properties (TestParameter)
4+
p_same = {...
5+
{"..", "./.."}, ...
6+
{"..", pwd() + "/.."}, ...
7+
{pwd(), pwd() + "/."}}
8+
9+
same_fun = {@stdlib.samepath, @stdlib.sys.samepath, @stdlib.python.samepath}
10+
end
11+
12+
methods(TestClassSetup)
13+
function pkg_path(tc)
14+
p = matlab.unittest.fixtures.PathFixture(fileparts(fileparts(mfilename('fullpath'))));
15+
tc.applyFixture(p)
16+
end
17+
end
18+
19+
methods(Test)
20+
21+
function test_samepath(tc, p_same, same_fun)
22+
is_capable(tc, same_fun)
23+
24+
tc.verifyTrue(same_fun(p_same{1}, p_same{2}))
25+
end
26+
27+
function test_samepath_notexist(tc, same_fun)
28+
is_capable(tc, same_fun)
29+
30+
tc.verifyFalse(same_fun("", ""))
31+
t = tempname();
32+
tc.verifyFalse(same_fun(t, t))
33+
end
34+
35+
end
36+
37+
end

test/TestSys.m

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -32,6 +32,15 @@ function test_is_admin(tc, is_admin_fun)
3232
tc.verifyNotEmpty(is_admin_fun())
3333
end
3434

35+
36+
function test_get_pid(tc)
37+
pid = stdlib.get_pid();
38+
39+
tc.verifyGreaterThan(pid, 0)
40+
tc.verifyClass(pid, 'uint64')
41+
end
42+
43+
3544
function test_get_shell(tc)
3645
tc.assumeFalse(tc.CI, "get_shell is not tested in CI due to platform differences")
3746
tc.verifyNotEmpty(stdlib.get_shell())

0 commit comments

Comments
 (0)