pythonmacosos.path

os.path.abspath() returning different results for same file


I am getting a different result for a file in os.path.abspath() when it is run in a unit test versus when it is run in the code being tested:

The code I am testing contains:

abspath = os.path.abspath(filepath)

My test for the code copies the file into the test directory and executes the code

self.test_data = os.path.dirname(test_data.__file__)
self.test_dir = tempfile.mkdtemp(prefix="test_")
shutil.copy(
    os.path.join(self.test_data, "thefile.txt"),
    os.path.join(self.test_dir, "thefile.txt"),
)

When the test is executed it fails. The code being tested produces this as the path for the test file:

/private/var/folders/1w/k19bxrj1463_4j5m8xv6_6cc0000gp/T/test_z66pgwj_/thefile.txt

but the test expects the path to be:

/var/folders/1w/k19bxrj1463_4j5m8xv6_6cc0000gp/T/test_z66pgwj_/thefile.txt

When I run os.path.abspath(os.path.join(self.test_dir, thefile.txt)) in the test it returns the same path without /private prepended.

I am using python 3.9.13 running on MacOS Monterey 12.6.2

EDIT: I have solved the issue, but the solution makes no sense to me:

os.path.abspath(os.path.join(self.test_dir, "thefile.txt")) returns:

/var/folders/1w/k19bxrj1463_4j5m8xv6_6cc0000gp/T/test_z66pgwj_/thefile.txt

os.path.abspath("thefile.txt) returns:

/private/var/folders/1w/k19bxrj1463_4j5m8xv6_6cc0000gp/T/test_z66pgwj_/thefile.txt


Solution

  • os.path.abspath() works with paths as common strings, it doesn't even check for path to exist. And /var/ and /tmp/ are symlinks on macOS:

     % ls -lh /tmp /var
    lrwxr-xr-x 1 root wheel 11 Jan 11 11:03 /tmp -> private/tmp
    lrwxr-xr-x 1 root wheel 11 Jan 11 11:03 /var -> private/var
    

    Therefore there's a bunch of tools returning unexpected (at first glance) results:

     % cd /tmp && touch "file.txt" && readlink -f file.txt
    
    /private/tmp/file.txt
    

    os.path.abspath(path) checks if path starts with /. If not, it calls os.getcwd() to get current dir, which on macOS returns the one with /private prefix. If path starts with /, os.getcwd() isn't called, and abspath() proceeds to normalize path string.