Can Julia always return the path to a directory with `/` at the end?

In Julia, sometimes a path to a directory is returned with / at the end, but sometimes not. For example,

julia> homedir()
"/users/xxxxx"

julia> joinpath(homedir(), "documents/")
"/users/xxxxx/documents/"

Here /users/xxxxx/documents/ is a directory, so is /users/xxxxx. But in the first line, it returns the result without the final /, while in the second line, it does. How can we unify this behavior? Can Julia always return the path, as long as it is a directory, with /; while if it is a file, without /?

No, please no. Some Linux tools like rsync, have different behavior depending on if the final slash is or not provided, this could introduce subtle intercommunication bugs.

3 Likes

OK, if so, can we have an issamepath function in Base? Because now, all paths are just strings, so if I want to compare them, "/users/xxxxx/documents/" is not equal to "/users/xxxxx/documents", and that means they are different paths. This is not what I want.
I know we have FilePathsBase.jl, but it would be really nice if this is implemented in Base.

What is your criteria for two paths being the same? Or better yet, does your definition of path includes single files? If documents is a file then "/users/xxxxx/documents" is a correct way to refer to it, and "/users/xxxxx/documents/" is an incorrect way.

I find funny that homedir() returns "/users/xxxxx" because this means isdirpath(homedir()) === false, Julia function isdirpath just checks if the path ends with a separator, it does not looks at the filesystem. Sincerely, it kinda seems to me that the Base filesystem utilities are kinda bad designed. But, perhaps, the problem is that there is a set of functions that avoid looking into the filesystem and another that use the information there to make inferences.

2 Likes

There is abspath(a) == abspath(b)

It does not work.

julia> abspath("/users/xxxxx/documents/") == abspath("/users/xxxxx/documents")
false
2 Likes

What is your criteria for two paths being the same?

My criterion is: if a path refers to a file, it should be ended without /; if it is a directory, it should be ended with /. So, if we want to check if "/users/xxxxx/documents/" and "/users/xxxxx/documents" are equal, we should check whether they are both directories, not just string comparison.

Sincerely, it kinda seems to me that the Base filesystem utilities are kinda bad designed.

We really should have an object-based (rather than string-based) path library in the standard libraries, like pathlib in Python.

1 Like

As you can see, standard library tools often end up being reconsidered but they cannot be improved or replaced, saddling the ecosystem with baggage I’d rather do without. using FilePathsBase is quite easy to type, especially if added to setup.jl.

I understand. I am just saying the current string-based path system was poorly designed and did not take inspiration from pathlib. I wish we can have that in Julia 2.0.

This does work though:

julia> realpath("/tmp") == realpath("/tmp/")
true
1 Like

So, should it be filesystem-aware? Or can it refer to things that do not exist (yet)? It is just a question of representation (all folder representations should always end with a /) or is it a question of matching what it is in the filesystem?

I ask because: if you let someone input a path to you, either they need to be aware of this representation detail, i.e., if the path refers to a folder, they are obligated to give this information by leaving a trailing slash; and it raises the question, what if they do not know if it is a folder beforehand, or do want the code to work generically for both alternatives? If it is not a matter of representation, the other problem is that the file/folder may not yet exist, and looking at the filesystem also does not give an answer.

A possible problem with this alternative is that if one of the paths describes something that do not exist, then the function call throws an error.

3 Likes

On windows, paths are separated with backslash but on unix you can make a regular file \

joinpath returns whatever you supply in the filename yourself.

You just happen to tell it to append “documents/” to homedir().

julia> joinpath(homedir(), "documents")
"/home/matt/documents"

julia> joinpath(homedir(), "documents\\")
"/home/matt/documents\\"

julia> joinpath(homedir(), "documents/")
"/home/matt/documents/"

Julia does not check if documents is an existing directory so cannot reliably append a directory separator.

1 Like

On your wider question of making the path dependent on the file system properties of the members of said path.

  1. For some filesystems folders can also be read like files.
  2. In distributed systems, file paths can be built without access to the file system
  3. mkpath("/this/does/not/exist/yet")
  4. stat is expensive
  5. stat is even more expensive when it is a distributed file system, it might be going to Mars and back!
  6. None of these things stop you using a filesystem aware module.

The Julia way is to have as few items in Base as is reasonable and be using anything else.

Personally I cannot see the utility. Obviously you do.

I think invoking a system call to the kernel to create a representation of a path is too much overhead and is something that should be under control of the programmer.

2 Likes