skip to Main Content

The operating system is Windows 10.

I downloaded the installer for Python 3.12.3 from https://www.python.org/downloads/.
During custom installation, I chose a install location: D:ProgramsPythonPython312

After installation, I confirmed that the D:ProgramsPythonPython312 directory exists, and that python.exe is in it. No Windows environment variables were changed by the installation.

I did not establish any Python virtual environments.

In Visual Studio 2017, I created a new C++ Windows console application project, named runpy. I changed the project settings so that it can find headers and libraries for the Python version I installed. The following changes are sufficient for the app to compile and link.

  • In C/C++ > General > Additional Include Directories, added: D:ProgramsPythonPython312include
  • In Linker > General > Additional Library Directories, added: D:ProgramsPythonPython312libs

Also, I manually copied python312_d.dll to the output directory of the app. i.e., from D:ProgramsPythonPython312 to D:workrunpyx64Debug. I will make that a postbuild step in the project later. That is sufficient for the app to run.

I attempted to embed Python in the C++ app with the following code.

// Precompiled header includes other stuff not relevant to embedding Python.
#include "stdafx.h"

// Standard stuff
#include <iostream>    // std::cout

// Python
#include <Python.h>



void on_PyStatusException( PyStatus const & status )
{
  std::cout << "PyStatus.func: " << ( status.func ? status.func : "n/a" ) << std::endl;
  std::cout << "PyStatus.err_msg: " <<  ( status.err_msg ? status.err_msg : "n/a" ) << std::endl;
  Py_ExitStatusException( status );
}



int main( int argc, char * argv[] )
{
  printf( "Howdy this is a C++ app that embeds Pythonn");
  PyStatus status;
  PyConfig config;

  //------------------------------
  // Sets config.isolated to 1, and other stuff.
  // But not sufficient by itself.
  PyConfig_InitIsolatedConfig( &config );

  // Unknown if important.  Returns a successful status.
  status = PyConfig_Read( &config );

  // Provide an explicit path to the Python executable.  Returns a successful status.
  wchar_t * pythonPath = L"D:\Programs\Python\Python312\python.exe";
  status = PyConfig_SetString( &config, &config.executable, pythonPath );

  //------------------------------
  // Does *not* work.  Returns a failure status.
  status = Py_InitializeFromConfig( &config );
  if ( PyStatus_Exception( status ) )
  {
    on_PyStatusException( status );
  }

  //------------------------------
  // Cleanup.
  PyConfig_Clear( &config );

  return 0;
}

However, Py_InitializeFromConfig() fails with the following output.

Python path configuration:
  PYTHONHOME = (not set)
  PYTHONPATH = (not set)
  program name = 'python'
  isolated = 1
  environment = 0
  user site = 0
  safe_path = 1
  import site = 1
  is in build tree = 0
  stdlib dir = 'D:workrunpyLib'
  sys._base_executable = 'D:\Programs\Python\Python312\python.exe'
  sys.base_prefix = 'D:\work\runpy'
  sys.base_exec_prefix = 'D:\work\runpy'
  sys.platlibdir = 'DLLs'
  sys.executable = 'D:\Programs\Python\Python312\python.exe'
  sys.prefix = 'D:\work\runpy'
  sys.exec_prefix = 'D:\work\runpy'
  sys.path = [
    'D:\work\runpy\x64\Debug\python312_d.zip',
    'D:\work\runpy',
    'D:\work\runpy\Lib',
    'D:\work\runpy\x64\Debug',
  ]
PyStatus.func: init_fs_encoding
PyStatus.err_msg: failed to get the Python codec of the filesystem encoding

Several of the Python path configuration settings in the above output are clearly wrong. They point to the directory of the runpy app, instead of to Python stuff. What other PyConfig properties should I establish so that Py_InitializeFromConfig() will succeed?

I have been following examples in other StackOverflow posts and the https://docs.python.org documentation, but I cannot find the correct combination.

2

Answers


  1. Chosen as BEST ANSWER

    As @AhmedAEK suggested, setting PyConfig.home instead of PyConfig.executable was the essential thing so that the subsequent call of Py_InitializeFromConfig() succeeds. Because I am using a later version of Python, I call PyConfig_SetString( &config, &config.home, value ) instead of Py_SetPythonHome().

    The following is my latest successful attempt to embed Python in the C++ app.

    // Precompiled header includes other stuff not relevant to embedding Python.
    #include "stdafx.h"
    
    // Standard stuff
    #include <iostream>    // std::cout
    
    // Python
    #include <Python.h>
    
    
    
    void on_PyStatusException( PyStatus const & status )
    {
      std::cout << "PyStatus.func: " << ( status.func ? status.func : "n/a" ) << std::endl;
      std::cout << "PyStatus.err_msg: " <<  ( status.err_msg ? status.err_msg : "n/a" ) << std::endl;
      Py_ExitStatusException( status );
    }
    
    
    
    int main( int argc, char * argv[] )
    {
      printf( "Howdy this is a C++ app that embeds Pythonn");
      PyStatus status;
      PyConfig config;
    
      //------------------------------
      // Sets config.isolated to 1, and other stuff.
      PyConfig_InitIsolatedConfig( &config );
    
      // Sets some stuff.  But unknown if important.
      PyConfig_Read( &config );
    
      // Change PYTHONHOME environment variable.
      wchar_t * value = L"D:\Programs\Python\Python312";
      PyConfig_SetString( &config, &config.home, value );
    
      //------------------------------
      // Initialize Python.
      status = Py_InitializeFromConfig( &config );
      if ( PyStatus_Exception( status ) )
      {
        on_PyStatusException( status );
      }
    
      //------------------------------
      // Dump system configuration that demonstrates that PYTHONHOME change had an effect.
      PyRun_SimpleString( "import sysnprint( f'{sys.prefix=}' );" );
    
      //------------------------------
      // Cleanup.
      PyConfig_Clear( &config );
    
      return 0;
    }
    

    The following is the output.

    Howdy this is a C++ app that embeds Python
    sys.prefix='D:\Programs\Python\Python312'
    

  2. Python needs to know where its standard library is located, you need to call Py_SetPythonHome, passing the path of the folder that contains python.exe, before calling Py_InitializeFromConfig.

    wchar_t pythonPath[]  = L"D:\Programs\Python\Python312";
    Py_SetPythonHome(pythonPath);
    

    Actually python.exe itself is not needed, only the Lib and DLLs folders are needed for an embedded interpreter.

    To make a smaller package, you can zip compress the entire Lib folder, as python can load .py files from .zip files, but the .pyd files in DLLs folder needs to be shipped along with your executable.

    if you set config.site_import = 0; you will disable the loading of the site package, which makes some of the libraries obsolete and can be removed for even smaller package, so long as you aren’t using anything you are deleting, which is not recommended unless you know exactly what you are doing.

    Login or Signup to reply.
Please signup or login to give your own answer.
Back To Top
Search