ArcMap Python Path – How to Programmatically Get the Path of ‘Python.exe’ Used by ArcMap

arcgis-10.0cpython

I am working with an add-in of ArcMap in C#. From C# code, I have executed some Python scripts. Now, to run those script, I have hard-coded python path. But this is not portable. So, I want to get the path of the Python executable from the code and use it.

Question:

How can I get the path of the Python executable used by ArcMap from C# code?

EDIT :

From your suggestions, for now I am using "path environment" to get Python path.

//get python path from environtment variable
string GetPythonPath()
{
    IDictionary environmentVariables = Environment.GetEnvironmentVariables();
    string pathVariable = environmentVariables["Path"] as string;
    if (pathVariable != null)
    {
        string[] allPaths = pathVariable.Split(';');
        foreach (var path in allPaths)
        {
            string pythonPathFromEnv = path + "\\python.exe";
            if (File.Exists(pythonPathFromEnv))
                return pythonPathFromEnv;
        }
    }
}

But there is a problem:

When different version of python is installed in my machine, there is no guarantee that, the "python.exe" I am using, ArcGIS also using that.

I don't appreciate using another tool to get "python.exe" path. So, I really think if there any way to get the path from registry key. For "ArcGIS10.0" registry looks like:
enter image description here

And for that, I am thinking about following way to get the path:

//get python path from registry key
string GetPythonPath()
{
    const string regKey = "Python";
    string pythonPath = null;
    try
    {
        RegistryKey registryKey = Registry.LocalMachine;
        RegistryKey subKey = registryKey.OpenSubKey("SOFTWARE");
        if (subKey == null)
            return null;

        RegistryKey esriKey = subKey.OpenSubKey("ESRI");
        if (esriKey == null)
            return null;

        string[] subkeyNames = esriKey.GetSubKeyNames();//get all keys under "ESRI" key
        int index = -1;
     /*"Python" key contains arcgis version no in its name. So, the key name may be 
     varied version to version. For ArcGIS10.0, key name is: "Python10.0". So, from
     here I can get ArcGIS version also*/
        for (int i = 0; i < subkeyNames.Length; i++)
        {
            if (subkeyNames[i].Contains("Python"))
            {
                index = i;
                break;
            }
        }
        if(index < 0)
            return null;
        RegistryKey pythonKey = esriKey.OpenSubKey(subkeyNames[index]);

        string arcgisVersion = subkeyNames[index].Remove(0, 6); //remove "python" and get the version
        var pythonValue = pythonKey.GetValue("Python") as string;
        if (pythonValue != "True")//I guessed the true value for python says python is installed with ArcGIS.
            return;

        var pythonDirectory = pythonKey.GetValue("PythonDir") as string;
        if (pythonDirectory != null && Directory.Exists(pythonDirectory))
        {
            string pythonPathFromReg = pythonDirectory + "ArcGIS" + arcgisVersion + "\\python.exe";
            if (File.Exists(pythonPathFromReg))
                pythonPath = pythonPathFromReg;
        }  
    }
    catch (Exception e)
    {
        MessageBox.Show(e + "\r\nReading registry " + regKey.ToUpper());
        pythonPath = null;
    }
    return pythonPath ;
}

But before using the second procedure, I need to be sure about my guesses. Guesses are:

  1. the "True" associated with python means python is installed with ArcGIS
  2. ArcGIS 10.0 and upper version's registry key will be written in same process.

Please help me to get any clarification about my guesses.

Best Answer

Instead of looking for the Python executable, this help topic suggests shelling out to cmd.exe and running python.exe without qualifying its location. Note however, that this should work because the ArcGIS Desktop installer sets up (edit: recently tested at 10.1, it doesn't) relies upon the path to python.exe being added to the user's PATH environment variable.

Another approach is to create a script tool and execute it from ArcObjects.

If you are really after the path to ArcGIS's version of python.exe, by extension of the ArcObjects + script tool approach, you could create a Python script tool whose only output is the value of sys.exec_prefix. This is the path of the folder containing ArcGIS's version of Python, e.g. C:\Python27\ArcGIS10.1.

Side note: sys.executable returns the path to ArcMap.exe and NOT python.exe when run in-process, which is why I do not suggest using that variable.

Call the script tool from ArcObjects and get the output from the returned IGeoProcessorResult object.

Update: Here is a sample ArcMap add-in project (VS2010, .NET 3.5) that uses a script tool packaged within the add-in that simply displays the path to the python.exe used by ArcMap: http://wfurl.com/cbd5091

It's just a button you click and it pops up a messagebox with the path:

Button MessageBox

The interesting bits of code:

  • Python script:

    import sys
    import os
    import arcpy
    
    def getPythonPath():
        pydir = sys.exec_prefix
        pyexe = os.path.join(pydir, "python.exe")
        if os.path.exists(pyexe):
            return pyexe
        else:
            raise RuntimeError("No python.exe found in {0}".format(pydir))
    
    if __name__ == "__main__":
        pyexe = getPythonPath()
        arcpy.AddMessage("Python Path: {0}".format(pyexe))
        arcpy.SetParameterAsText(0, pyexe)
    
  • C# function:

    public string GetPythonPath()
    {
        // Build the path to the PythonPathToolbox
        string toolboxPath = Path.Combine(Path.GetDirectoryName(this.GetType().Assembly.Location), "PythonPath.tbx");
    
        // Initialize the geoprocessor.
        IGeoProcessor2 gp = new ESRI.ArcGIS.Geoprocessing.GeoProcessorClass();
    
        // Add the PythonPath toolbox.
        gp.AddToolbox(toolboxPath);
    
        // Need an empty array even though we have no input parameters
        IVariantArray parameters = new VarArrayClass();
    
        // Execute the model tool by name.
        var result = gp.Execute("GetPythonPath", parameters, null);
        return result.GetOutput(0).GetAsText();
    }
    
Related Question