Python equivalent of Perl file test readable (-r), writeable (-w) and executable (-x) operators


Question

I have been Googling to try and find an equivalent in Python to some of Perl's file test operators.

Most of the file test operators are just a direct Pythonification of the underlying os' stat call. For example, os.stat('file').st_ctime just reads the inode change time as the *nix stat utility or ls -l would do.

Some of the Perl file test operators I cannot find an equivalent in Python. For example, I have a data tree of 85,000 image files created by a variety of applications. Some of the files have the effective UID set in a way that is nettlesome and a modification fails for a permission issue. So for those files I need to run something like:

$ find . -type f -print0 | perl -0 -lnE 'say unless -w' | change euid...

Since I have not found the equivalent in Python, I have to shell out to Perl to find these files. I found this table which suggests there is no direct equivalent. True?

1
11
12/18/2010 9:39:22 PM

Accepted Answer

Looking at the output of strace, perl does a stat() call followed by getgroups() to get the supplementary group IDs of the perl process. So it seems it just checks the results of the stat() call against the EUID, EGID and supplementary group IDs.

Python has a getgroups() function in os, so I'm sure you could do the same.

EDIT: You could try something like this if nobody comes up with a better answer. (Hardly tested):

def effectively_readable(path):
    import os, stat

    uid = os.getuid()
    euid = os.geteuid()
    gid = os.getgid()
    egid = os.getegid()

    # This is probably true most of the time, so just let os.access()
    # handle it.  Avoids potential bugs in the rest of this function.
    if uid == euid and gid == egid:
        return os.access(path, os.R_OK)

    st = os.stat(path)

    # This may be wrong depending on the semantics of your OS.
    # i.e. if the file is -------r--, does the owner have access or not?
    if st.st_uid == euid:
        return st.st_mode & stat.S_IRUSR != 0

    # See comment for UID check above.
    groups = os.getgroups()
    if st.st_gid == egid or st.st_gid in groups:
        return st.st_mode & stat.S_IRGRP != 0

    return st.st_mode & stat.S_IROTH != 0

Obviously the -w one would be almost identical, but with W_OK, S_IWUSR, etc.

10
12/19/2010 11:26:29 AM

Starting with Python 3.3 you can do this with os.access:

Changed in version 3.3: Added the dir_fd, effective_ids, and follow_symlinks parameters.

If effective_ids is True, access() will perform its access checks using the effective uid/gid instead of the real uid/gid. effective_ids may not be supported on your platform; you can check whether or not it is available using os.supports_effective_ids. If it is unavailable, using it will raise a NotImplementedError.


Licensed under: CC-BY-SA with attribution
Not affiliated with: Stack Overflow
Icon