Appending trailing slashes to paths

Won’t it be useful to always append trailing slashes to paths for all core path functions? We then would see at first glance if a path leads to a directory or a file. When you see for example ~/config, you can’t tell if this is a file or a directory – the implementation of this convention would clarify this.

Some additional thoughts:

  • Currently we have both, as normpath(@__FILE__, "..") shows a trailing slash, whereas normpath(@__DIR) (or just @__DIR__) show no trailing slash. This could be unified.

  • dir * "/" * file would then expand to "<DIR>//<FILE>". That doubled slash makes no difference, which makes this backwards-compatible.

  • It should be better for Windows compatibility, as I assume dir * "/" * file wouldn’t work there, whereas dir * file (with trailing slash convention) should do the trick, as this should automatically use a backslash.

  • dir * "/" * "var" could expand to /var if dir is empty – which could have negative impact on system roots.

You should use joinpath though.

2 Likes

“You should do it right” solves every problem on earth, hooray!

I know about joinpath(), still a LOT of people (especially beginners) use … * "/" * …. Everything can be further polished, and I think that this would make things clearer and improves some situation for beginners without introducing negative consequneces. If you are against, then please describe at least a downside argument.

1 Like

I don’t know if this is true, but if anyone is handling paths like this, then they shouldn’t. This means that your last 3 examples are not very relevant.

Paths have existence independently of the file system. You cannot tell if "/some/path" is a file, directory, or neither, until you examine it (eg isdir), except for some cases (isdirpath).

1 Like

They should also not do memory bugs in C and null pointer exceptions in Java…

An example – I just stumbled upon this thread here:

I know this is not necessary, but it would make Julia more nice, see this screen shot of my Juno session:

Bildschirmfoto%20von%202018-10-05%2015-22-02

Is it a folder or a file? With normpath() adding a trailing slash, you would be able to infer that! Yeah, isfile() is 100% precise, but here a trailing slash just makes it more comfortable and you get maybe 90% precision instead of currently none at all. And again, I don’t see a reason not to do that.

Why not? Windows accepts forward slashes as path separators (except for command-line tools), even if backslashes are more idiomatic.

My specific point was this: if you use string concatenation instead of joinpath, it may work, but it can also miss some corner cases.

1 Like

For what it’s worth, neither Python, Ruby nor bash append the trailing /. (Well, for bash it of course depends a bit on the context, but for example pwd doesn’t append.) If I had to take a guess I’d say it’s probably very standard not to append it.

I’m not necessarily arguing Julia should follow precedents set by those languages (in many cases it does not for good reason and the result is wonderful), but I don’t personally see any compelling reason to buck the trend here, particularly when Julia has a pretty extensive set of file management functionality in Base.

If anything, it might be worth investigating why normpath(dir,"..") appends trailing slash, as that would seem to be the non-standard thing here. Even weirder is that it only seems to do that when you use "..". It doesn’t seem like a big deal, but it’s always nice to have predictable behavior.

For bash, it would definitely be very bad practice to append a / automatically, since many command line tools (eg cp and rsync) do something completely different depending on whether you append one.

FWIW, I think that conditioning on a trailing / is an unfortunate design choice (and consequently a source of many bugs), but it looks like it is established practice, so I am not sure Julia would benefit from automatically appending a / either — it would make run(`something $(withpath)`) tricky.

2 Likes

I guess it might have been nice if dir/ were standard notation for a directory to denote that fact that it’s a directory. We’re 40 years too late on that decision though :laughing:

3 Likes

Wouldn’t mv <file> <dir> in a shell actually be better and safer when dir contains a trailing slash? This way you cannot as easily accidentally mix files with folders in commands, so that should be more precise and a bit like type-safety…? When you want to move something in a directory and use a path without a trailing slash, mv would think that this is a file instead, or? (Sorry, I’m not really into shell scripting…)

May be a bug. I tried to emulate Python behavior closely here, but normpath is pretty tricky to implement.

1 Like

Guilty as charged… Did not know I was doing this wrong and should have been using joinpath. You learn something new every day…

Does anybody think a Path or File or Dir type would be useful?

I’ve had quite a few instances where I wanted methods in the line of:
load_data(file_name::String)
load_data(data::String)

When dealing with these I had to come up with artificial solutions when what I really would’ve wanted was to dispatch on a Path type (or File or Dir):
load_data(file_name::File)

This would also address the “is it a file or a dir” question and would have distinct show, print etc functions which could take into account end slashes, extensions, etc.

5 Likes

There is GitHub - rofinn/FilePaths.jl: A type based approach to working with filesystem paths in julia, and it also has been discussed on github to include this kinda thing in base (but I cannot find it).

1 Like

There was also a work-in-progress Julep for this idea: https://github.com/JuliaLang/Juleps/pull/26. Didn’t get far enough to make it into Julia 1.0 but still an interesting idea that would be worthy of someone exploring fully to see how nice one can make working with paths with this approach.

I am not saying that the interface to mv etc is well-designed (quite the opposite), but at this point some tools acting differently depending on the trailing / is pretty much a given for Julia.

It depends on isdirpath:

julia> path = "/tmp/testdir/";isdirpath(path),normpath(path)
(true, "/tmp/testdir/")

julia> path = "/tmp/testdir/.";isdirpath(path),normpath(path)
(true, "/tmp/testdir/")

julia> path = "/tmp/testdir/..";isdirpath(path),normpath(path)
(true, "/tmp/")

julia> path = "/tmp/testdir";isdirpath(path),normpath(path)
(false, "/tmp/testdir")

@StefanKarpinski Probably not relevant here, but do you look at os.path or pathlib? I’ve heard that os.path has some issues that cannot be fixed due to backward compatibility guarantee. It looks like pathlib got those things right. PEP 428 – The pathlib module – object-oriented filesystem paths | peps.python.org

Nope. Mostly looks like it requires a special path type, which we could still do.

1 Like