You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
{{ message }}
Repository navigation
properly define what "canonical" means in os.path.realpath #134639
This function emulates the operating system's procedure for making a path
canonical, which differs slightly between Windows and UNIX with respect
to how links and subsequent path components interact.
IMO, it should be clearly documented what it does or at least:
what it guarantees to the least (For example will the pathname be absolute? Will it be normalised?)
and/or:
which OS function it’s identical to, like on POSIX where that would probably realpath(), and whether there are differences to that
Especially since IMO "canonical" pathname (though I think it has no formal definition, or is there anything in POSIX?) means rather just that there are no symlinks left, i.e. that the file is "reached" by its true (=canonical) name.
But that could still be a relative pathname, and perhaps even one that is not normalised.
abspath returns "a normalize absolutized path", implying that there can be more than one for a given file. I undestand realpath to return the canonical abspath, so that path-a and path-b are the same file iff realpath(path-a) == realpath(path-b).
Hmm. I think even abspath will always return the same result (for the same string), so the a is perhaps also a bit misleading as there are not more than one possible result.
It does however not resolve symbolic links, so the result may simply point to another pathname (as documented).
Your example is true, though IMO canonical does not mean whether or not the resulting paths are then relative (I would however say the word implies normalised).
In your example, assuming a cwd of /home and a path-a of /tmp and a path-b of ../tmp, the equality would still be true if realpath would return ../tmp or /../tmp.
From my testing, realpath always returns absolute and normalised pathnames (as well as any symlinks being resolved, which is however already documented).
What I'm a bit unsure about how to deal with is, that it also normalises absolute pathnames with exactly two leading /:
>>> os.path.realpath("//tmp",strict=True)
'/tmp'
Which in pure POSIX is IMO wrong, as // has a special meaning (though Linux - and probably all others? - handles it like normal paths).
Some Python library parts do in fact account for this, e.g.
I undestand realpath to return the canonical abspath, so that path-a and path-b are the same file iff realpath(path-a) == realpath(path-b).
Yes, or more geneally, if you take two paths, say $p_a$ and $p_b$ that point to file system objects $f_a$ and $f_b$ respectively, then their canonical form $c_a$ and $c_b$ respectively compare equal if and only if $f_a$ and $f_b$ are the same.
In the returned string, symbolic links are resolved, as are . and .. pathname components.
Consecutive slash (/) characters are replaced by a single slash.
FTR, realpath is meant to emulate the realpath from coreutils.
Even then it should be documented what exactly that (i.e. emulate) means... like is it really meant to be the exact same behaviour or just "similar" in 99,9% cases.
As for your argument (I think you mean realpath() from libc? not realpath from coreutils (and meanwhile also POSIX)), one should then probably check whether that behaviour of glibc is actually even intended or also merely "ignorant" of the standard.
And, as I've laid out in #138139, even if glibc does it on purpose, there might be still other platforms that don't (and which os.path.realpath should then handle like that platform... but as I've also written there, even then it would introduce some nasty incompatibility with other Python stdlib code (e.g pathlib.Path)... and what if someone compares e.g. the results of the two?
Activity
terryjreedy commentedon May 24, 2025
abspathreturns "a normalize absolutized path", implying that there can be more than one for a given file. I undestandrealpathto return the canonical abspath, so thatpath-aandpath-bare the same file iffrealpath(path-a) == realpath(path-b).calestyo commentedon May 24, 2025
Hmm. I think even
abspathwill always return the same result (for the same string), so theais perhaps also a bit misleading as there are not more than one possible result.It does however not resolve symbolic links, so the result may simply point to another pathname (as documented).
Your example is true, though IMO canonical does not mean whether or not the resulting paths are then relative (I would however say the word implies normalised).
In your example, assuming a cwd of
/homeand apath-aof/tmpand apath-bof../tmp, the equality would still be true ifrealpathwould return../tmpor/../tmp.From my testing,
realpathalways returns absolute and normalised pathnames (as well as any symlinks being resolved, which is however already documented).What I'm a bit unsure about how to deal with is, that it also normalises absolute pathnames with exactly two leading
/:Which in pure POSIX is IMO wrong, as
//has a special meaning (though Linux - and probably all others? - handles it like normal paths).Some Python library parts do in fact account for this, e.g.
So I'm tempted to say, that
os.path.realpath("//tmp",strict=True)should yield'//tmp'.Unless anyone strongly disagrees, I'd open a separate issue for that particular aspect.
pythongh-134639: Clarify what "canonical" means in os.path.realpath
picnixz commentedon Aug 25, 2025
Yes, or more geneally, if you take two paths, say$p_a$ and $p_b$ that point to file system objects $f_a$ and $f_b$ respectively, then their canonical form $c_a$ and $c_b$ respectively compare equal if and only if $f_a$ and $f_b$ are the same.
picnixz commentedon Aug 25, 2025
For that, please open a separate issue indeed. Changing it is a breaking change so we really need to be careful.
picnixz commentedon Aug 25, 2025
FTR,
realpathis meant to emulate the realpath from coreutils. Note that canonicalization has a manpage: https://man7.org/linux/man-pages/man3/canonicalize_file_name.3.html which says:In particular:
prints
/tmpand not//tmp.calestyo commentedon Aug 25, 2025
Done so in #138139. :-)
Even then it should be documented what exactly that (i.e.
emulate) means... like is it really meant to be the exact same behaviour or just "similar" in 99,9% cases.As for your argument (I think you mean
realpath()from libc? notrealpathfrom coreutils (and meanwhile also POSIX)), one should then probably check whether that behaviour of glibc is actually even intended or also merely "ignorant" of the standard.And, as I've laid out in #138139, even if glibc does it on purpose, there might be still other platforms that don't (and which
os.path.realpathshould then handle like that platform... but as I've also written there, even then it would introduce some nasty incompatibility with other Python stdlib code (e.gpathlib.Path)... and what if someone compares e.g. the results of the two?picnixz commentedon Aug 25, 2025
Both of them. They behave similarly (see https://manpages.debian.org/testing/coreutils/realpath.1.en.html). And both of them output
/tmpwhen given//tmp.