Skip to content

Commit

Permalink
Provide new 'is_exe' function used by 'which'
Browse files Browse the repository at this point in the history
As noted by bug report http://bugs.python.org/issue14706,

"This is not a Python bug. os.access() is just a wrapper
 around the POSIX access() function"

http://pubs.opengroup.org/onlinepubs/9699919799/functions/faccessat.html

""" If any access permissions are checked, each shall be
    checked individually, as described in XBD File Access
    Permissions, except that where that description refers
    to execute permission for a process with appropriate
    privileges, an implementation may indicate success for
    X_OK even if execute permission is not granted to any
    user.
"""
  • Loading branch information
jquast committed Jun 8, 2014
1 parent cb7cbd2 commit 11af2e9
Showing 1 changed file with 38 additions and 5 deletions.
43 changes: 38 additions & 5 deletions pexpect/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -80,6 +80,7 @@
import traceback
import signal
import codecs
import stat
except ImportError: # pragma: no cover
err = sys.exc_info()[1]
raise ImportError(str(err) + '''
Expand Down Expand Up @@ -1957,15 +1958,48 @@ def search(self, buffer, freshlen, searchwindowsize=None):
return best_index


def is_exe(fname):
# follow symlinks,
fpath = os.path.realpath(fname)

# return False for non-files (directories, fifo, etc.)
if not os.path.isfile(fpath):
return False

# On Solaris, etc., "If the process has appropriate privileges, an
# implementation may indicate success for X_OK even if none of the
# execute file permission bits are set."
#
# For this reason, it is necessary to explicitly check st_mode

# get file mode using os.stat, and check if `other',
# that is anybody, may read and execute.
mode = os.stat(fpath).st_mode
if mode & stat.S_IROTH and mode & stat.S_IXOTH:
return True

# get current user's group ids, and check if `group',
# when matching ours, may read and execute.
user_gids = os.getgroups() + [os.getgid()]
if (os.stat(fpath).st_gid in user_gids and
mode & stat.S_IRGRP and mode & stat.S_IXGRP):
return True

# finally, if file owner matches our effective userid,
# check if `user', may read and execute.
user_gids = os.getgroups() + [os.getgid()]
if (os.stat(fpath).st_uid == os.geteuid() and
mode & stat.S_IRUSR and mode & stat.S_IXUSR):
return True


def which(filename):
'''This takes a given filename; tries to find it in the environment path;
then checks if it is executable. This returns the full path to the filename
if found and executable. Otherwise this returns None.'''

# Special case where filename contains an explicit path.
if (os.path.dirname(filename) != '' and
os.access(filename, os.X_OK) and
os.path.isfile(os.path.realpath(filename))):
if os.path.dirname(filename) != '' and is_exe(filename):
return filename
if 'PATH' not in os.environ or os.environ['PATH'] == '':
p = os.defpath
Expand All @@ -1974,8 +2008,7 @@ def which(filename):
pathlist = p.split(os.pathsep)
for path in pathlist:
ff = os.path.join(path, filename)
if (os.access(ff, os.X_OK) and
os.path.isfile(os.path.realpath(ff))):
if is_exe(ff):
return ff
return None

Expand Down

0 comments on commit 11af2e9

Please sign in to comment.