Take the 2-minute tour ×
Geographic Information Systems Stack Exchange is a question and answer site for cartographers, geographers and GIS professionals. It's 100% free, no registration required.

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 a 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. Thanks in advance.

share|improve this question
5  
Have you considered creating a script tool and executing it from ArcObjects? –  blah238 Dec 20 '12 at 19:19
3  
Can you not just have a PATH environmental variable set to the ArcGIS Python exe be a installation requirement for your Add-In? –  Chad Cooper Dec 22 '12 at 3:58
    
All said, @ChadCooper's thought has to be the best way. Rather than try to work backwards, just set it once and for all at install-time. –  elrobis Dec 22 '12 at 15:18
    
@elrobis: I know, setting the path in PATH environment is a good way. But I wanted to know if there any way to find the python and do everything without interrupting the user. –  Emi Dec 23 '12 at 3:26
    
@blah238 thanks for your suggestion. I never worked with script tool. May be I need to learn about it –  Emi Dec 23 '12 at 3:29
add comment

5 Answers

up vote 2 down vote accepted

I took your second code example, made it work on both 64 and 32-bit OS's, and simplified it a bit. Works for me at 10.1 on Windows 7 64-bit, but obviously you should test it on as many environments as possible, and add back in whatever defensive programming checks you think are necessary.

After testing clean-installing ArcGIS Desktop 10.1 without Python, I found that it does not include the Python10.x subkey, let alone the "Python" True/False value (still not sure what that is for, maybe contact ESRI support if you must know).

string GetPythonPath()
{
    string pythonPath = null;
    var localmachineKey = Registry.LocalMachine;
    // Check whether we are on a 64-bit OS by checking for the Wow6432Node key (32-bit version of the Software registry key)
    var softwareKey = localmachineKey.OpenSubKey(@"SOFTWARE\Wow6432Node"); // This is the correct key for 64-bit OS's
    if (softwareKey == null) {
        softwareKey = localmachineKey.OpenSubKey("SOFTWARE"); // This is the correct key for 32-bit OS's
    }
    var esriKey = softwareKey.OpenSubKey("ESRI");
    var realVersion = (string)esriKey.OpenSubKey("ArcGIS").GetValue("RealVersion"); // Get the "real", canonical version of ArcGIS
    var shortVersion = String.Join(".", realVersion.Split('.').Take(2).ToArray()); // Get just the Major.Minor part of the version number, e.g. 10.1
    var pythonKey = esriKey.OpenSubKey("Python" + shortVersion); // Open the Python10.x sub-key
    if (pythonKey == null) {
        throw new InvalidOperationException("Python not installed with ArcGIS!");
    }
    var pythonDirectory = (string)pythonKey.GetValue("PythonDir");
    if (Directory.Exists(pythonDirectory))
    {
        // Build path to python.exe
        string pythonPathFromReg = Path.Combine(Path.Combine(pythonDirectory, "ArcGIS" + shortVersion), "python.exe");
        if (File.Exists(pythonPathFromReg)) {
            pythonPath = pythonPathFromReg;
        }
    }
    return pythonPath;
}

On a Desktop 10.1 machine with Python, this returns C:\Python27\ArcGIS10.1\python.exe. On a Desktop 10.1 machine without Python, this raises an InvalidOperationException due to the Python10.x key not being present.

Hopefully this helps you with whatever you're trying to actually accomplish, which is -- amazingly -- still not clear to me.

share|improve this answer
add comment

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();
    }
    
share|improve this answer
2  
But this document does not says that, ArcGIS Desktop installer sets up the path to python.exe to the user's PATH environment variable. So it can be possible that the path of python is not in the PATH environment variable. Then it will create an error. So, how can I be sure that the python executable path is in user's PATH environment variable. –  Emi Dec 20 '12 at 8:40
2  
You can't, like most things in life and computing all you can do is make assumptions and hope things work, and have a fallback plan when they don't (provide instructions on adding it to the PATH environment variable). That said, if it's a normal installation, I believe the ArcGIS Desktop installer adds that path to the PATH environment variable. –  blah238 Dec 20 '12 at 8:55
2  
I've seen many installations where the arcgis-installed python was not in the path. And what if there are two versions installed and the 'wrong' one is in the path? –  blindJesse Dec 22 '12 at 5:11
    
I offered a solution in my 3rd paragraph that ought to find ArcGIS's installation of Python regardless of the PATH environment variable. –  blah238 Dec 22 '12 at 5:42
    
@blah238 doesn't creating script tool make my add-in less portable or make the installation process of the add-in to others machine difficult? –  Emi Dec 23 '12 at 7:59
show 6 more comments

I'd like to offer up a alternate solution, based on my comment in the question above. For a current project, I am doing something very similar; I have a .NET Add-In that, when the user clicks a button in the ArcMap UI, a Python script runs. I made having a PATH environmental variable set to the ArcGIS Python executable a requirement, that way I don't really have to worry about including the path to the Python exe in my .NET code.

Now, while in development, testers are just setting up the PATH variable manually. But I eventually will have a Windows installer (exe) created that will install the Add-In, install any Python dependencies, and set any needed PATH variables. For this, I am using the Nullsoft Scriptable Install System (NSIS), an open source system for creating Windows installers. Here is some the code that I have worked up so far, which is pretty rough. Basically, it looks in the registry to see if the PATH variables of interest are there, and if they aren't it adds them. Has to be run as admin, of course.

include "StrFunc.nsh"
!include "LogicLib.nsh"

/*
  Name: VIESORE_Installer.nsi
  Author: Chad Cooper, CAST
  Date: 7/16/2012
  Purpose: NSIS installer script for .exe creation by NSIS. Installs VIESORE components and sets up environment.
*/

Name "VIESORE"
Caption "VIESORE Installer"
Outfile "VIESOREInstaller.exe"

RequestExecutionLevel admin

# Initialize functions
${StrLoc}
# Initialize user variables
Var path

Section "Set SYSTEM PATH environmental variables"
    ReadRegStr $0 HKLM "SYSTEM\CurrentControlSet\Control\Session Manager\Environment" "Path"
    ${StrLoc} $1 $0 "C:\Python26\ArcGIS10.0" ">"
    ${StrLoc} $2 $0 "C:\Python26\ArcGIS10.0\Scripts" ">"
        ${StrLoc} $3 $0 "C:\Python26\ArcGIS10.0\Lib\site-packages" ">"
        ${StrLoc} $4 $0 "C:\Program Files\e-on software\Vue 10 Infinite\Application" ">"
        # Test to see if env vars exist in current system PATH, if not add them to $path variable
        ${If} $3 == ""
                StrCpy $path "C:\Python26\ArcGIS10.0\Lib\site-packages"
        ${EndIf}
        ${If} $2 == ""
                StrCpy $path "C:\Python26\ArcGIS10.0\Scripts;$path"
        ${EndIf}
        ${If} $1 == ""
                StrCpy $path "C:\Python26\ArcGIS10.0;$path"
        ${EndIf}
        ${If} $4 == ""
                StrCpy $path "C:\Program Files\e-on software\Vue 10 Infinite\Application;$path"
        ${EndIf}
        DetailPrint "$path written to system PATH"
    WriteRegStr HKLM "SYSTEM\CurrentControlSet\Control\Session Manager\Environment" "Path" "$0;$path"
    ReadRegStr $5 HKLM "SYSTEM\CurrentControlSet\Control\Session Manager\Environment" "Path"
    DetailPrint "New Path: $5"
SectionEnd

So, again, this doesn't find the path to the ArcGIS Python exe, but it allows you to give your end user the power to set it properly and easily.

share|improve this answer
    
+1 I completely agree with this recommendation---where at the high-level, Chad is saying "don't work the problem backward to deduce Arc's python instance, rather use an installer and leave it to the SysAdmin to establish the correct python instance." @ChadCooper, does NSIS give you any UI control so that you can override those default paths if you choose? I'm not seeing that implied by the code but I'm betting it's there. –  elrobis Dec 24 '12 at 18:14
    
@elrobis - I bet you can override/edit/change existing ones - NSIS is very configurable and can let you build a pretty slick installer - you just gotta figure out the code to write it. –  Chad Cooper Dec 24 '12 at 18:22
    
Creating an installer for an add-in just seems a little crazy. Also, what modifications would you need to make to support 10.1, 10.2, etc. as well as 10.0? –  blah238 Dec 24 '12 at 21:18
    
@blah238 - yeah, it does seem crazy, but my installer for this particular project will be doing lots more, as a talked about. My Add-In is strictly for 10.0. I guess for different versions of ArcGIS you could check the registry to see what version is installed, then act accordingly. –  Chad Cooper Dec 24 '12 at 21:41
add comment

[Edit] While executing set programmatically (struck out, below) did what I wanted, it can be accomplished more easily and with cleaner code using Environment.GetEnvironmentVariables().

One option would be to scan every Environment Variable on the system and attempt to prove the following:

1) Is the Environment Variable value a directory? (and if so..)

2) Does that directory contain python.exe?

I was able to do this programmatically by executing the set command through the .Net Process API. The set command, when used without a parameter, returns ALL the Environment Variables in use by the system. So I could parase, then organize the STDOUT results emitted from set, and screen them to see if anything whatsoever (and I mean ANYTHING) available through the system envionment ultimately pointed to python.exe.

From this page discussing the set command:

Type SET without parameters to display all the current environment variables.

To illustrate I wrote a combination of methods (and a helper class) that does what I discussed above. These can be optimized, and they could use some bullet-proofing (Try..Catch, etc.), but if the computer has ANY Environment Variable pointing to python.exe, this approach should find it! I don't care if the var is called PATH, ABBRACADABBRA, or whatever.. if it points to python.exe, this should find it.

// C#, you'll need these using statements:
using System;
using System.Collections.Generic;
using System.Diagnostics;
using System.IO;
using System.Text.RegularExpressions;

Here terms is an Array of strings you pass-in to the routine to look for in either the environment variable's name, or its n values (i.e. PATH can have several values, but most other vars will have just one). Make sure all the strings in terms are UPPERCASE!

(When I tested this, I used simply "PYTHON", which found C:\Python27\python.exe on my home system. But you could easily extend it to include another string[] of terms if you wanted to further inspect the path of any python.exe candidates returned---for example, to see if they were in the ArcGIS bin, etc.)

// Top-level method that organizes everything below..
private void scrapeEnvironmentVariables(string[] terms)
{
    // !! ValueObject !! This is a Helper Class, find it at the bottom..
    List<ValueObject> voList = buildListOfEnvironmentObjects();

    foreach (ValueObject vo in voList)
    {
        bool candidateFound = ObjectMatchesSearchTerms(vo, terms);

        if (candidateFound)
        {    
            string exeCandidate = "";
            foreach (string unlikelyPath in vo.values)
            {
                if (Directory.Exists(unlikelyPath))
                {
                    string unlikelyExe = unlikelyPath + "\\python.exe";
                    if(File.Exists(unlikelyExe))
                        exeCandidate = unlikelyExe;
                }

                if (exeCandidate != "")
                {
                    break;
                    // At this point, exeCandidate is a fully-qualified
                    // path to python.exe..
                }
            }

            // If you only want the first hit, break here..
            // As-is, the code will look for even more matches.
            //if (breakOnFirstHit)
            //    break;
        }
    }
}


// Execute Environment.GetEnvironmentVariables() and organize the 
// key..value pairs into 1:n ValueObjects (see Helper Class below).
private List<ValueObject> buildListOfEnvironmentObjects()
{
    // Return a List of 1:n key..value objects.
    List<ValueObject> voList = new List<ValueObject>();

    IDictionary variableDictionary = Environment.GetEnvironmentVariables();
    foreach (DictionaryEntry entry in variableDictionary)
    {
        // Explode multi-values into a List of values (n).
        List<string> values = new List<string>();
        string[] rawValues = ((string)entry.Value).Split(';');
        foreach (string value in rawValues)
            if (value != "") values.Add(value.ToUpper());

        ValueObject valueObject = new ValueObject();
        valueObject.key = ((string)entry.Key).ToUpper();
        valueObject.values = values.ToArray();

        voList.Add(valueObject);
    }
    return voList;
}


// Compare the key and any value(s) in a ValueObject with all the
// terms submitted to the top-level method. If **ALL** the terms
// match (against any combination of key..value), it returns true.
private bool ObjectMatchesSearchTerms(ValueObject vo, string[] terms)
{
    int matchCount = 0;

    foreach (string term in terms)
    {
        if (vo.key.Contains(term))              // screen the key
            matchCount++;

        foreach (string value in vo.values)     // screen N values
        {
            if (value.Contains(term))
                matchCount++;
        }
    }

    // Test against >= because it's possible the match count could
    // exceed the terms length, like if a match occurred in both the
    // key and the value(s). So >= avoids omiting that possibility.
    return (matchCount >= terms.Length) ? true : false;
}    

And at the bottom of my main class, I included the following Helper Class:

class ValueObject : Object
{
    public ValueObject() { } // default constructor

    public string key;
    public string[] values;
}
share|improve this answer
    
This is fragile, as the user can customize the Python installation directory in the ArcGIS Desktop installer. Also the PYTHONPATH variable is NOT the one you want. –  blah238 Dec 20 '12 at 18:13
    
@blah238, sometimes fragile is all you've got. I was actually surprised to see that Arc wired-up against PYTHONPATH. That's a default 9.2 install. Nevertheless, the OP asked how to programmatically get to the ArcGIS python.exe, and the approach I recommended, fragile or not, does that. –  elrobis Dec 20 '12 at 19:33
    
Can't say I understand the down-vote, is this answer really "not useful"? It might not be awesome, but it's certainly an option, would probably work for a typical Arc install, and at the very least, it adds something helpful to the thread--specifically, it illustrates a default Arc install opting to link its python.exe with an Environment Variable other than PATH. –  elrobis Dec 20 '12 at 20:17
    
Sorry but you are incorrect. The PYTHONPATH variable is used by Python to find modules, not by ArcGIS to find Python. Check the link. –  blah238 Dec 20 '12 at 21:32
    
@blah238, I think the screenshot was overwhelming/obscurring the point I was trying to make. (Specifically, my suggestion to the OP wasn't intending to emphasize PYTHONPATH, that just happened to be the only var on that particular system pointing back to python.exe.) Anyway, I overhauled my answer to include a working C# code example, and I would appreciate knowing if you still disagree with this approach. Thanks /E. –  elrobis Dec 21 '12 at 11:17
show 4 more comments

Will you have access to the registry?

When installing ArcMap it will install Python if it can't find it. It looks in the registry to see if Python is already installed. I believe the standard registry location for this is: computer\HKEY_LOCAL_MACHINE\SOFTWARE\PYTHON\PythonCore\2.7\InstallPath With a default Key of the Path location (2.7 being 10.1, 2.6 being 10.0)

I cant think of a reason when/why the value of this key would be incorrect, but you could always go this way: Inside the Esri\Desktop hive of the registry is a Python location. Its the simple path which you could get and then build up further paths to ensure there is a Python.exe. For example, the key on a 64bit machine gets installed to: computer\HKEY_LOCAL_MACHINE\SOFTWARE\Wow6432Node\ESRI\Python10.1 With a PythonDir key and associated Path value

But I like @blah238 answer. Just open a prompt from your program and run it there. I can't see a reason why this wouldnt work.

share|improve this answer
1  
This approach is also flawed because there could be multiple Python installations and you have no straightforward way of programmatically determining which one is the one used by ArcGIS. If you are not running arcpy this might not matter though. Still, I think the most robust solution would involve the registry and quite a bit of logic. I am not going to go there though. –  blah238 Dec 20 '12 at 21:38
    
Well the logic would have to start with the newest version 2.7 and work backwards. This of course could fail if you installed a new version of Python, then went and installed an older version of ArcGIS which would install an older version of Python. So yeah, I agree theres this potential, but its not likely (or you could just construct a lookup of arc10.1 = py2.7, arc10.0=py26...etc to be 100% sure). As I said, the best method would probably just to shell out to command prompt. –  KHibma Dec 20 '12 at 22:15
    
@KHibma I searched through the registry. But I think it's really bothersome if I look through "PYTHON" key. In my machine, there are two python version installed and it returns both. I think, its a good idea to look through "ESRI" key and if there is a "Python" subkey with true value, then I can take the "PythonDir" subkey value. It works in my case :) –  Emi Dec 23 '12 at 5:06
    
Is this a wrong way to search python path in registry through "ESRI" key? Or is there any chance that, the way esri uses to create keys and values in registry can be change and the code maybe can not read it –  Emi Dec 23 '12 at 6:02
    
When I say "search" the registry, I mean use the actual paths I provided above. Unless someone knows better, these reg keys location (in the registry) wouldnt change from machine to machine. So you'd just hardcode the paths to see if the keys exist there, if so whats the value... –  KHibma Jan 3 '13 at 18:51
add comment

Your Answer

 
discard

By posting your answer, you agree to the privacy policy and terms of service.

Not the answer you're looking for? Browse other questions tagged or ask your own question.