`unexpanduser` counterpart to `os.path.expanduser`

Hey.

Would there be any interest in adding a counterpart for `os.path.expanduser` like:

def unexpanduser(path):
    home_directory = os.path.expanduser("~")
    if os.path.isabs(home_directory)  and  os.path.isabs(path)  and  os.path.commonpath((home_directory, path)) == home_directory:
        relative_path = os.path.relpath(path, start=home_directory)
        return "~"  if relative_path == "."  else os.path.join("~", relative_path)
    else:
        return path

I need this from time to time in order to make pathnames more pretty and thought it might be of general use.

The os.path.isabs(home_directory) is needed in case the os.path.expanduser("~") doesn’t find the homedir and returns "~" again (and for weird cases where the homedir would be relative).

The os.path.isabs(path) is needed if the input path is relative.

Other than that, pretty straight forward and I could try make a PR (and perhaps even add tests :stuck_out_tongue: ) if there’d be some consensus that this should be added to os.path.

Cheers,
Chris.

What do you do if 2 users have the same home directory? Or if the path is /a/b/c, and one user’s home directory is /a and another’s is /a/b?

Have you checked PyPI? This sounds like something that might be there.

Are these cases even a problem?
I mean we (un)resolve to ~ (not to ~user) which can obviously only ever be valid or the respective current user – if another user uses the resulting path, the results are undefined, just as everywhere with these pathnames.

And it wouldn’t surprise me if it’s on PyPI… the question here was really whether it would be beneficial to have it in the standard library – I’m totally fine if the consensus is “no”. :slight_smile:

If it’s in the stdlib, everything is a problem!

Maybe I’m misunderstanding what you’re proposing. Could you specify the behavior, without relying on code? I would expect such a function, given “/user/foo” to return “~foo”, assuming user foo’s home directory is “/home/foo”, irrespective of what user calls it.

Yes… the idea is about the following behaviour (with my homedir being /home/calestyo):

>>> unexpanduser("tmp")
'tmp'

>>> unexpanduser("/tmp")
'/tmp'

>>> unexpanduser("/home/calestyo")
'~'

>>> unexpanduser("///home/calestyo/")
'~'

>>> unexpanduser("///home/./calestyo/")
'~'

>>> unexpanduser("/home/calestyo/foo")
'~/foo'

>>> unexpanduser("../../home/calestyo/")
'../../home/calestyo/'

I think all are more or less straight forward, except perhaps the last one, which is in the current implementation ignored because it’s a relative link.

I would tend to say that this is the behaviour one mostly wants from such a function, which is really intended for making the strings “prettier”.
That’s also the reason why the path tmp from a CWD that is the homedir is not changed to ~/.

I shall also note, that I think all of the functions I use operate purely on the strings and never look anything up in the filesystem and I’d say this is also desired, consider e.g.

>>> unexpanduser("/home/../home/calestyo/foo")
'/home/../home/calestyo/foo'

where IMO we do not want to have a replacement, because the .. could give different results when symlinks are used.

Now even if anther UID also has the same homedir, I think nothing changes, or does it?
Same if mine is beneath another homedir or another homedir is in-between.

Cheers,
Chris.

Would it be able to resolve other users’ home directories, eg unexpanduser("/root") being "~root", when logged in as “testuser”?