[Bf-blender-cvs] [3459969d8c5] pygpu_extensions: PyAPI: Use PyPreConfig & PyConfig for Python initialization

Campbell Barton noreply at git.blender.org
Fri Feb 12 16:17:20 CET 2021


Commit: 3459969d8c502471531c0b183face9b61119217a
Author: Campbell Barton
Date:   Fri Feb 12 08:08:17 2021 +1100
Branches: pygpu_extensions
https://developer.blender.org/rB3459969d8c502471531c0b183face9b61119217a

PyAPI: Use PyPreConfig & PyConfig for Python initialization

Use Python 3.8's API for setting the initial configuration.

This replaces a mix of our logic and direct calls to the Python API
and has no user visible changes.

Using the Python API makes the logic easier to follow and provides
utilities such as `PyConfig_SetBytesArgv`
that wasn't available in previous releases.

Note that this uses Python's utf8/wchar_t conversions,
which used to cause problems (see T31506).

Since `Py_UTF8Mode` was set, the systems locale isn't used for decoding,
allowing us to use Python's utility functions that call
`Py_DecodeLocale` internally.

Ref D10382

===================================================================

M	source/blender/python/generic/py_capi_utils.c
M	source/blender/python/generic/py_capi_utils.h
M	source/blender/python/intern/bpy_interface.c

===================================================================

diff --git a/source/blender/python/generic/py_capi_utils.c b/source/blender/python/generic/py_capi_utils.c
index 02c8ed30baf..5aff24356e9 100644
--- a/source/blender/python/generic/py_capi_utils.c
+++ b/source/blender/python/generic/py_capi_utils.c
@@ -889,40 +889,6 @@ void PyC_MainModule_Restore(PyObject *main_mod)
   Py_XDECREF(main_mod);
 }
 
-/** \} */
-
-/* -------------------------------------------------------------------- */
-/** \name #Py_SetPythonHome Wrapper
- * \{ */
-
-/**
- * - Must be called before #Py_Initialize.
- * - Expects output of `BKE_appdir_folder_id(BLENDER_PYTHON, NULL)`.
- * - Note that the `PYTHONPATH` environment variable isn't reliable, see T31506.
- *   Use #Py_SetPythonHome instead.
- */
-void PyC_SetHomePath(const char *py_path_bundle)
-{
-#  ifdef __APPLE__
-  /* OSX allow file/directory names to contain : character (represented as / in the Finder)
-   * but current Python lib (release 3.1.1) doesn't handle these correctly */
-  if (strchr(py_path_bundle, ':')) {
-    fprintf(stderr,
-            "Warning! Blender application is located in a path containing ':' or '/' chars\n"
-            "This may make python import function fail\n");
-  }
-#  endif
-
-  /* Set the environment path. */
-  wchar_t py_path_bundle_wchar[1024];
-
-  /* Can't use `mbstowcs` on linux gives bug: T23018. */
-  BLI_strncpy_wchar_from_utf8(
-      py_path_bundle_wchar, py_path_bundle, ARRAY_SIZE(py_path_bundle_wchar));
-
-  Py_SetPythonHome(py_path_bundle_wchar);
-}
-
 bool PyC_IsInterpreterActive(void)
 {
   /* instead of PyThreadState_Get, which calls Py_FatalError */
diff --git a/source/blender/python/generic/py_capi_utils.h b/source/blender/python/generic/py_capi_utils.h
index 4b895dea6a1..c9f2008b013 100644
--- a/source/blender/python/generic/py_capi_utils.h
+++ b/source/blender/python/generic/py_capi_utils.h
@@ -88,8 +88,6 @@ bool PyC_NameSpace_ImportArray(PyObject *py_dict, const char *imports[]);
 void PyC_MainModule_Backup(PyObject **r_main_mod);
 void PyC_MainModule_Restore(PyObject *main_mod);
 
-void PyC_SetHomePath(const char *py_path_bundle);
-
 bool PyC_IsInterpreterActive(void);
 
 void *PyC_RNA_AsPointer(PyObject *value, const char *type_name);
diff --git a/source/blender/python/intern/bpy_interface.c b/source/blender/python/intern/bpy_interface.c
index c4789407e4e..311612e1499 100644
--- a/source/blender/python/intern/bpy_interface.c
+++ b/source/blender/python/intern/bpy_interface.c
@@ -304,100 +304,150 @@ static struct _inittab bpy_internal_modules[] = {
     {NULL, NULL},
 };
 
+/**
+ * Convenience function for #BPY_python_start.
+ *
+ * These should happen so rarely that having comprehensive errors isn't needed.
+ * For example if `sys.argv` fails to allocate memory.
+ *
+ * Show an error just to avoid silent failure in the unlikely event something goes wrong,
+ * in this case a developer will need to track down the root cause.
+ */
+static void pystatus_exit_on_error(PyStatus status)
+{
+  if (UNLIKELY(PyStatus_Exception(status))) {
+    fputs("Internal error initializing Python!\n", stderr);
+    /* This calls `exit`. */
+    Py_ExitStatusException(status);
+  }
+}
+
 /* call BPY_context_set first */
 void BPY_python_start(bContext *C, int argc, const char **argv)
 {
 #ifndef WITH_PYTHON_MODULE
-  PyThreadState *py_tstate = NULL;
 
-  /* Needed for Python's initialization for portable Python installations.
-   * We could use #Py_SetPath, but this overrides Python's internal logic
-   * for calculating it's own module search paths.
-   *
-   * `sys.executable` is overwritten after initialization to the Python binary. */
+  /* #PyPreConfig (early-configuration).  */
   {
-    const char *program_path = BKE_appdir_program_path();
-    wchar_t program_path_wchar[FILE_MAX];
-    BLI_strncpy_wchar_from_utf8(program_path_wchar, program_path, ARRAY_SIZE(program_path_wchar));
-    Py_SetProgramName(program_path_wchar);
-  }
-
-  /* must run before python initializes */
-  PyImport_ExtendInittab(bpy_internal_modules);
+    PyPreConfig preconfig;
+    PyStatus status;
 
-  /* Allow to use our own included Python. `py_path_bundle` may be NULL. */
-  {
-    const char *py_path_bundle = BKE_appdir_folder_id(BLENDER_SYSTEM_PYTHON, NULL);
-    if (py_path_bundle != NULL) {
-      PyC_SetHomePath(py_path_bundle);
+    if (py_use_system_env) {
+      PyPreConfig_InitPythonConfig(&preconfig);
     }
     else {
-      /* Common enough to use the system Python on Linux/Unix, warn on other systems. */
-#  if defined(__APPLE__) || defined(_WIN32)
-      fprintf(stderr,
-              "Bundled Python not found and is expected on this platform "
-              "(the 'install' target may have not been built)\n");
-#  endif
+      /* Only use the systems environment variables and site when explicitly requested.
+       * Since an incorrect 'PYTHONPATH' causes difficult to debug errors, see: T72807.
+       * An alternative to setting `preconfig.use_environment = 0` */
+      PyPreConfig_InitIsolatedConfig(&preconfig);
     }
-  }
-
-  /* Force `utf-8` on all platforms, since this is what's used for Blender's internal strings,
-   * providing consistent encoding behavior across all Blender installations.
-   *
-   * This also uses the `surrogateescape` error handler ensures any unexpected bytes are escaped
-   * instead of raising an error.
-   *
-   * Without this `sys.getfilesystemencoding()` and `sys.stdout` for example may be set to ASCII
-   * or some other encoding - where printing some `utf-8` values will raise an error.
-   *
-   * This can cause scripts to fail entirely on some systems.
-   *
-   * This assignment is the equivalent of enabling the `PYTHONUTF8` environment variable.
-   * See `PEP-540` for details on exactly what this changes. */
-  Py_UTF8Mode = 1;
 
-  /* Suppress error messages when calculating the module search path.
-   * While harmless, it's noisy. */
-  Py_FrozenFlag = 1;
-
-  /* Only use the systems environment variables and site when explicitly requested.
-   * Since an incorrect 'PYTHONPATH' causes difficult to debug errors, see: T72807. */
-  Py_IgnoreEnvironmentFlag = !py_use_system_env;
-  Py_NoUserSiteDirectory = !py_use_system_env;
-
-  /* Initialize Python (also acquires lock). */
-  Py_Initialize();
+    /* Force `utf-8` on all platforms, since this is what's used for Blender's internal strings,
+     * providing consistent encoding behavior across all Blender installations.
+     *
+     * This also uses the `surrogateescape` error handler ensures any unexpected bytes are escaped
+     * instead of raising an error.
+     *
+     * Without this `sys.getfilesystemencoding()` and `sys.stdout` for example may be set to ASCII
+     * or some other encoding - where printing some `utf-8` values will raise an error.
+     *
+     * This can cause scripts to fail entirely on some systems.
+     *
+     * This assignment is the equivalent of enabling the `PYTHONUTF8` environment variable.
+     * See `PEP-540` for details on exactly what this changes. */
+    preconfig.utf8_mode = true;
+
+    /* Note that there is no reason to call #Py_PreInitializeFromBytesArgs here
+     * as this is only used so that command line arguments can be handled by Python itself,
+     * not for setting `sys.argv` (handled below). */
+    status = Py_PreInitialize(&preconfig);
+    pystatus_exit_on_error(status);
+  }
+
+  /* Must run before python initializes, but after #PyPreConfig. */
+  PyImport_ExtendInittab(bpy_internal_modules);
 
-  /* We could convert to #wchar_t then pass to #PySys_SetArgv (or use #PyConfig in Python 3.8+).
-   * However this risks introducing subtle changes in encoding that are hard to track down.
-   *
-   * So rely on #PyC_UnicodeFromByte since it's a tried & true way of getting paths
-   * that include non `utf-8` compatible characters, see: T20021. */
+  /* #PyConfig (initialize Python). */
   {
-    PyObject *py_argv = PyList_New(argc);
-    for (int i = 0; i < argc; i++) {
-      PyList_SET_ITEM(py_argv, i, PyC_UnicodeFromByte(argv[i]));
+    PyConfig config;
+    PyStatus status;
+    bool has_python_executable = false;
+
+    PyConfig_InitPythonConfig(&config);
+
+    /* Suppress error messages when calculating the module search path.
+     * While harmless, it's noisy. */
+    config.pathconfig_warnings = 0;
+
+    /* When using the system's Python, allow the site-directory as well. */
+    config.user_site_directory = py_use_system_env;
+
+    /* While `sys.argv` is set, we don't want Python to interpret it. */
+    config.parse_argv = 0;
+    status = PyConfig_SetBytesArgv(&config, argc, (char *const *)argv);
+    pystatus_exit_on_error(status);
+
+    /* Needed for Python's initialization for portable Python installations.
+     * We could use #Py_SetPath, but this overrides Python's internal logic
+     * for calculating it's own module search paths.
+     *
+     * `sys.executable` is overwritten after initialization to the Python binary. */
+    {
+      const char *program_path = BKE_appdir_program_path();
+      status = PyConfig_SetBytesString(&config, &config.program_name, program_path);
+      pystatus_exit_on_error(status);
     }
-    PySys_SetObject("argv", py_argv);
-    Py_DECREF(py_argv);
-  }
 
-  /* Setting the program name is important so the 'multiprocessing' module
-   * can launch new Python instances. */
-  {
-    const char *sys_variable = "executable";
-    char program_path[FILE_MAX];
-    if (BKE_appdir_program_python_search(
-            program_path, sizeof(program_path), PY_MAJOR_VERSION, PY_MINOR_VERSION)) {
-      PyObject *py_program_path = PyC_UnicodeFromByte(program_path);
-      PySys_SetObject(sys_variable, py_program_path);
-      Py_DECREF(py_program_path);
+    /* Setting the program name is important so the 'multiprocessing' module
+     * can launch new Python instances. */
+    {
+      char program_path[FILE_MAX];
+      if (BKE_appdir_program_python_search(
+              program_path, sizeof(program_path), PY_MAJOR_VERSION, PY_MINOR_VERSION)) {
+        status = PyConfig_SetBytesString(&config, &config.executable, program_path);
+        pystatus_exit_on_error(status);
+        has_python_executable = true;
+      }
+      else {
+        /* S

@@ Diff output truncated at 10240 characters. @@



More information about the Bf-blender-cvs mailing list