Check if a program exists from a python script

shutil.which

Let me recommend an option that has not been discussed yet: a Python implementation of which, specifically shutil.which. It was introduced in Python 3.3 and is cross-platform, supporting Linux, Mac, and Windows. It is also available in Python 2.x via whichcraft. You can also just rip the code for which right out of whichcraft here and insert it into your program.

def is_tool(name):
    """Check whether `name` is on PATH and marked as executable."""

    # from whichcraft import which
    from shutil import which

    return which(name) is not None

distutils.spawn.find_executable

Another option that has already been mentioned is distutils.spawn.find_executable.

find_executable's docstring is as follows:

Tries to find 'executable' in the directories listed in 'path'

So if you pay attention, you'll note that the name of the function is somewhat misleading. Unlike which, find_executable does not actually verify that executable is marked as executable, only that it is on the PATH. So it's entirely possible (however unlikely) that find_executable indicates a program is available when it is not.

For example, suppose you have a file /usr/bin/wget that is not marked executable. Running wget from the shell will result in the following error: bash: /usr/bin/wget: Permission denied. which('wget') is not None will return False, yet find_executable('wget') is not None will return True. You can probably get away with using either function, but this is just something to be aware of with find_executable.

def is_tool(name):
    """Check whether `name` is on PATH."""

    from distutils.spawn import find_executable

    return find_executable(name) is not None

The easiest way is to try to run the program with the desired parameters, and handle the exception if it doesn't exist:

try:
    subprocess.call(["wget", "your", "parameters", "here"])
except FileNotFoundError:
    # handle file not found error.

This is a common pattern in Python: EAFP

In Python 2, you had to catch OsError instead, since the more fine-grained exception classes for OS errors did not exist yet:

try:
    subprocess.call(["wget", "your", "parameters", "here"])
except OSError as e:
    if e.errno == errno.ENOENT:
        # handle file not found error.
    else:
        # Something else went wrong while trying to run `wget`
        raise

Tags:

Python