Source code for dkfileutils.which
# -*- coding: utf-8 -*-
"""Print where on the path an executable is located.
"""
from __future__ import print_function
import sys
import os
from stat import ST_MODE, S_IXUSR, S_IXGRP, S_IXOTH
[docs]def get_executable(name):
"""Return the first executable on the path that matches `name`.
"""
for result in which(name):
return result
return None
[docs]def get_path_directories():
"""Return a list of all the directories on the path.
"""
pth = os.environ['PATH']
if sys.platform == 'win32' and os.environ.get("BASH"):
# winbash has a bug..
if pth[1] == ';': # pragma: nocover
pth = pth.replace(';', ':', 1)
return [p.strip() for p in pth.split(os.pathsep) if p.strip()]
[docs]def is_executable(fname):
"""Check if a file is executable.
"""
return os.stat(fname)[ST_MODE] & (S_IXUSR | S_IXGRP | S_IXOTH)
def _listdir(pth, extensions):
"""Non-raising listdir."""
try:
return [fname for fname in os.listdir(pth)
if os.path.splitext(fname)[1] in extensions]
except OSError: # pragma: nocover
pass
def _normalize(pth):
return os.path.normcase(os.path.normpath(pth))
[docs]def which(filename, interactive=False, verbose=False):
"""Yield all executable files on path that matches `filename`.
"""
exe = [e.lower() for e in os.environ.get('PATHEXT', '').split(';')]
if sys.platform != 'win32': # pragma: nocover
exe.append('')
name, ext = os.path.splitext(filename)
has_extension = bool(ext)
if has_extension and ext.lower() not in exe:
raise ValueError("which can only search for executable files")
def match(filenames):
"""Returns the sorted subset of ``filenames`` that matches ``filename``.
"""
res = set()
for fname in filenames:
if fname == filename: # pragma: nocover
res.add(fname) # exact match
continue
fname_name, fname_ext = os.path.splitext(fname)
if fname_name == name and fname_ext.lower() in exe:
res.add(fname)
return sorted(res)
returnset = set()
found = False
for pth in get_path_directories():
if verbose: # pragma: nocover
print('checking pth..')
fnames = _listdir(pth, exe)
if not fnames:
continue
for m in match(fnames):
found_file = _normalize(os.path.join(pth, m))
if found_file not in returnset: # pragma: nocover
if is_executable(found_file):
yield found_file
returnset.add(found_file)
found = True
if not found and interactive: # pragma: nocover
print("Couldn't find %r anywhere on the path.." % filename)
sys.exit(1)
if __name__ == "__main__": # pragma: nocover
_args = sys.argv
for _fname in which(_args[1], interactive=True, verbose='-v' in _args):
print(_fname)
sys.exit(0)