Skip to content

Instantly share code, notes, and snippets.

@ericsnowcurrently
Last active February 26, 2024 17:11
Show Gist options
  • Save ericsnowcurrently/9e694983a1174a17c7a6454a9dcdf888 to your computer and use it in GitHub Desktop.
Save ericsnowcurrently/9e694983a1174a17c7a6454a9dcdf888 to your computer and use it in GitHub Desktop.
A rough, high-level greenfield design for the CPython runtime API

Summary:

  • C-API takes opaque context type as first arg
  • context is as specific as possible
  • separate Py_Main() from runtime initialization
  • separate global init from interpreter init
  • explicit enabling/disabling runtime components

Background Info

(expand)

Global Runtime

initialization:

(expand)
  1. update config from env vars & CLI

  2. init hash randomization

  3. copy PyImport_Initab

  4. allow creating interpreters

  5. initialize main interpreter (part 1)

    1. create interpreter
    2. copy PyConfig
  6. set _PyRuntime->gilstate.autoInterpreterState

  7. initialize main interpreter (part 2)

    1. set feature flags (from PyInterpreterConfig)
    2. init obmalloc state
    3. create main thread state (and bind)
    4. initialize GIL (and take it)
  8. init global float state

  9. initialize main interpreter (part 3)

    1. init interned str dict
  10. init statically defined str objects

  11. init global str state

  12. initialize main interpreter (part 4)

  13. init interned str dict

  14. init interp->dtoa.p5s

  15. init (almost) all the builtin types

  16. init the typevar heap types (and cache them)

  17. init Int_InfoType

  18. str: init EncodingMapType

  19. str: init PyFieldNameIter_Type

  20. str: init PyFormatterIter_Type

  21. initialize FloatInfoType

  22. init the static builtin exception types

  23. preallocate some memory errors

  24. init interp->exc_state.errnomap

  25. init UnraisableHookArgsType

  26. set PyContextToken_Type.MISSING

  27. init _PyExc_InterpreterError

  28. init _PyExc_InterpreterNotFoundError

  29. init interp->warnings.filters

  30. init interp->warnings.once_registry

  31. init interp->warnings.default_action

  32. init interp->atexit

  33. init sys module 1. init sys.modules 2. create sys 3. set interp->sysdict 4. set interp->sysdict_copy 5. set sys.modules 6. set sys.stderr, sys.__stderr__ (preliminary) 7. set sys.displayhook, sys.__displayhook__ 8. set sys.excepthook, sys.__excepthook__ 9. set sys.breakpointhook, sys.__breakpointhook__ 10. set sys.unraisablehook, sys.__unraisablehook__ 11. set sys.version 12. set sys.hexversion 13. set sys._git 14. set sys._framework 15. set sys.api_version 16. set sys.copyright 17. set sys.platform 18. set sys.maxsize 19. set sys.float_info 20. set sys.int_info 21. init Hash_InfoType 22. set sys.hash_info 23. set sys.maxunicode 24. set sys.builtin_module_names 25. set sys.stdlib_module_names 26. set sys.byteorder 27. set sys.dllhandle 28. set sys.winver 29. set sys.abiflags 30. init VersionInfoType 31. set sys.version_info 32. init ImplementationType 33. set sys.implementation 34. init FlagsType 35. init and set sys.flags 36. init WindowsVersionType 37. set sys._vpath 38. set sys.float_repr_style 39. set sys.thread_info 40. init AsyncGenHooksType 41. init EmscriptenInfoType 42. set sys._emscripten_info 43. set sys.meta_path 44. set sys.path_importer_cache 45. set sys.path_hooks 46. _PyImport_FixupBuiltin("sys") 47. set sys.monitoring

  34. initialize the "builtins" module 1. _PyBuiltin_Init() 2. _PyImport_FixupBuiltin("builtins") 3. set interp->callable_cache.isinstance 4. set interp->callable_cache.len 5. set interp->callable_cache.list_append 6. set interp->callable_cache.object__getattribute__ 7. _PyBuiltins_AddExceptions() 8. set interp->builtins_copy 9. _PyImport_InitDefaultImportFunc()

  35. init the global XID registry

  36. init the XID registry

  37. init "core" import system 1. load _frozen_importlib (importlib._bootstrap) 2. set interp->imports.importlib 3. load imp 4. call _frozen_imporlib._install()

  38. _PyConfig_InitImportConfig()

  39. interpreter_update_config()

  40. _PyImport_InitExternal()

  41. _PyFaulthandler_Init()

  42. _PyUnicode_InitEncodings()

  43. _PySignal_Init()

  44. _PyTraceMalloc_Start()

  45. _PyPerfTrampoline_SetCallbacks()

  46. _PyPerfTrampoline_Init()

  47. init_sys_streams()

  48. init_set_builtins_open()

  49. run_presite()

  50. add_main_module()

  51. PyImport_ImportModule("warnings") (validates sys.warnoptions?)

  52. init_import_site()

  53. on Windows, emit_stderr_warning_for_legacy_locale()

  54. created and set the ceval optimizer

finalization:

(expand)
  1. ...

responsibilities of "main" interpreter:

  • ...

"main" interpreter possibilities:

  • the first one given to the user (status quo)
  • the active one in the main thread
  • not exposed to users
  • users must explicitly ask for it
  • explicitly declared by the user

Interpreter Runtime

initialization:

(expand)
  1. create interpreter

  2. copy PyConfig from calling interpreter (or main)

  3. set feature flags (from PyInterpreterConfig)

  4. init obmalloc state

  5. create (temp) thread state (and bind)

  6. initialize GIL (and take it)

  7. init interned str dict

  8. init interp->dtoa.p5s

  9. init (almost) all the non-exc static builtin types

  10. init the typevar heap types (and cache them)

  11. init Int_InfoType

  12. str: init EncodingMapType

  13. str: init PyFieldNameIter_Type

  14. str: init PyFormatterIter_Type

  15. initialize FloatInfoType

  16. init the static builtin exception types

  17. init interp->exc_state.errnomap

  18. init UnraisableHookArgsType

  19. init _PyExc_InterpreterError

  20. init _PyExc_InterpreterNotFoundError

  21. init interp->warnings.filters

  22. init interp->warnings.once_registry

  23. init interp->warnings.default_action

  24. init interp->atexit

  25. init sys module

  26. init sys.modules

  27. create sys

  28. set interp->sysdict

  29. set interp->sysdict_copy

  30. set sys.modules

  31. set sys.stderr, sys.__stderr__ (preliminary)

  32. set sys.displayhook, sys.__displayhook__

  33. set sys.excepthook, sys.__excepthook__

  34. set sys.breakpointhook, sys.__breakpointhook__

  35. set sys.unraisablehook, sys.__unraisablehook__

  36. set sys.version

  37. set sys.hexversion

  38. set sys._git

  39. set sys._framework

  40. set sys.api_version

  41. set sys.copyright

  42. set sys.platform

  43. set sys.maxsize

  44. set sys.float_info

  45. set sys.int_info

  46. init Hash_InfoType

  47. set sys.hash_info

  48. set sys.maxunicode

  49. set sys.builtin_module_names

  50. set sys.stdlib_module_names

  51. set sys.byteorder

  52. set sys.dllhandle

  53. set sys.winver

  54. set sys.abiflags

  55. init VersionInfoType

  56. set sys.version_info

  57. init ImplementationType

  58. set sys.implementation

  59. init FlagsType

  60. init and set sys.flags

  61. init WindowsVersionType

  62. set sys._vpath

  63. set sys.float_repr_style

  64. set sys.thread_info

  65. init AsyncGenHooksType

  66. init EmscriptenInfoType

  67. set sys._emscripten_info

  68. set sys.meta_path

  69. set sys.path_importer_cache

  70. set sys.path_hooks

  71. _PyImport_FixupBuiltin("sys")

  72. set sys.monitoring

  73. initialize the "builtins" module

  74. _PyBuiltin_Init()

  75. _PyImport_FixupBuiltin("builtins")

  76. set interp->callable_cache.isinstance

  77. set interp->callable_cache.len

  78. set interp->callable_cache.list_append

  79. set interp->callable_cache.object__getattribute__

  80. _PyBuiltins_AddExceptions()

  81. set interp->builtins_copy

  82. _PyImport_InitDefaultImportFunc()

  83. init the global XID registry

  84. init the XID registry

  85. init "core" import system

  86. load _frozen_importlib (importlib._bootstrap)

  87. set interp->imports.importlib

  88. load imp

  89. call _frozen_imporlib._install()

  90. _PyConfig_InitImportConfig()

  91. interpreter_update_config()

  92. _PyImport_InitExternal()

  93. _PyUnicode_InitEncodings()

  94. init_sys_streams()

  95. init_set_builtins_open()

  96. run_presite()

  97. add_main_module()

  98. init_import_site()

  99. set sys.path[0] (

finalization:

(expand)
  1. ...

Capabilities/Components

sources:

  • docs
  • sys modules
  • PyConfig
  • builtin modules
  • stdlib modules
  • C-API
  • Steve's rings & layers proposal (specifically, the layers)
    • opt stdlib -> req stdlib -> platform adaption -> core

components:

(expand)
  • build info
  • CLI (main)
  • core (global)
    • builtin objects (singletons, types, instances)
    • exception machinery
    • warnings machinery
    • debug utils
  • core (interp)
    • import system
      • builtin modules
      • frozen modules
      • "external" modules
        • sys.path
    • "site" (and user site)
    • Python compiler
    • Python bytecode interpreter
    • profiling
    • tracing
    • multiple interpreters (cross-interpreter)
  • platform
    • locale/encoding
    • allocators
    • hash
    • cryptographic RNG
    • filesystem
    • stdio
    • signals
    • threads
    • fork
    • subprocesses
    • network
    • users
    • security
    • devices
    • virtualization
  • stdlib (required)
  • stdlib (optional)
    • full set of text codecs
  • ...

API

Contexts

A "context" is essentially an opaque handle to some runtime context. The idea is that all CPython's API take a specific context type as its first argument.

(expand for code)
/* A high-level brain dump of a possible top-level CPython API.  */


/****************************/
/* CPython's main() */
/****************************/

typedef struct main_config {
    //PyRuntime_base_config_t runtimecfg,
    //PyInterpreter_base_config_t interpcfg,
    //...
} PyMain_config_t;

typedef struct main_args {
    int argc;
    wchar_t **argv;
    PyMain_config_t config;
} PyMain_args_t;

struct main_context;
typedef struct main_context * PyMain_context_t;

PyAPI_FUNC(void) PyMain_context_zero(PyMain_context_t *);
PyAPI_FUNC(int) PyMain_context_is_zero(PyMain_context_t);

PyAPI_FUNC(Py_error_t) PyMain_Initialize(
    PyMain_args_t args,
    PyMain_context_t *p_res);
PyAPI_FUNC(Py_error_t) PyMain_Finalize(PyMain_context_t);


/****************************/
/* global runtime */
/****************************/

// XXX Distinct init config vs. fini config?
// XXX PyRuntime_config_t?
typedef struct runtime_base_config {
    //...
} PyRuntime_base_config_t;

struct runtime_base_ctx;
typedef struct runtime_base_ctx * PyRuntime_base_context_t;

/* For now, this fails if a runtime has already been initialized. */
PyAPI_FUNC(Py_error_t) PyRuntime_Initialize(
    PyRuntime_base_config_t *cfg,
    PyRuntime_base_context_t *p_res);
/* This fails if the base context has un-finalized components. */
PyAPI_FUNC(Py_error_t) PyRuntime_Finalize(PyRuntime_base_context_t);


/****************************/
/* global components */
/****************************/

/* signals */

struct runtime_signals_ctx;
typedef struct runtime_signals_ctx * PyRuntime_signals_context_t;

PyAPI_FUNC(Py_error_t) PyRuntime_InstallSignalHandlers(
    PyRuntime_base_context_t runtime,
    PyInterpreter_thread_context_t *main,
    PyRuntime_signals_context_t *p_res);
PyAPI_FUNC(Py_error_t) PyRuntime_UninstallSignalHandlers(
    PyRuntime_signals_context_t);

/* ... */

//typedef ... PyRuntime_XXX_context_t;
//
//PyAPI_FUNC(Py_error_t) PyRuntime_EnableXXX(
//    PyRuntime_base_context_t base,
//    PyRuntime_XXX_config_t *cfg.
//    PyRuntime_XXX_context_t *p_res);
//PyAPI_FUNC(Py_error_t) PyRuntime_DisableXXX(PyRuntime_XXX_context_t);


/****************************/
/* interpreter runtime */
/****************************/

// XXX Distinct init config vs. fini config?
// XXX PyInterpreter_config_t?
typedef struct interp_base_config {
    //...
} PyInterpreter_base_config_t;

struct interp_base_ctx;
typedef struct interp_base_ctx * PyInterpreter_base_context_t;
struct interp_combined_ctx;
typedef struct interp_combined_ctx * PyInterpreter_context_t;

PyAPI_FUNC(Py_error_t) PyInterpreter_Initialize(
    PyRuntime_base_context_t interp,
    PyInterpreter_base_config_t *cfg,
    PyInterpreter_base_context_t *p_res);
PyAPI_FUNC(Py_error_t) PyInterpreter_Finalize(PyInterpreter_base_config_t);

PyAPI_FUNC(Py_error_t) PyInterpreter_GetContext(
    PyInterpreter_base_context_t *interp,
    PyInterpreter_context_t *p_res);
PyAPI_FUNC(Py_error_t) PyInterpreter_GetBaseContext(
    PyInterpreter_context_t *interp,
    PyInterpreter_base_context_t *p_res);


/****************************/
/* interpreter components */
/****************************/

/* compiler */

struct interp_compiler_ctx;
typedef struct interp_compiler_ctx * PyInterpreter_compiler_context_t;

PyAPI_FUNC(Py_error_t) PyInterpreter_EnableCompiler(
    PyInterpreter_base_context_t *interp,
    PyInterpreter_compiler_context_t ctx,
    const char *str,
    const char *filename,
    int start,
    PyObject **p_res);

/* bytecode interpreter */

struct interp_exec_ctx;
typedef struct interp_exec_ctx * PyInterpreter_exec_context_t;

PyAPI_FUNC(Py_error_t) PyEval_EvalCode(
    PyInterpreter_exec_context_t ctx,
    PyObject *co,
    PyObject *globals,
    PyObject *locals,
    PyObject **p_res);

/* multi-threading */

struct interp_threading_ctx;
typedef struct interp_threading_ctx * PyInterpreter_threading_context_t;

PyAPI_FUNC(Py_error_t) PyInterpreter_EnableMultiThreading(
    PyInterpreter_base_context_t *interp,
    PyInterpreter_threading_context_t *p_res);
PyAPI_FUNC(Py_error_t) PyInterpreter_DisableMultiThreading(
    PyInterpreter_threading_context_t)

struct thread_nonlocal_ctx;
typedef struct thread_nonlocal_ctx * PyThread_nonlocal_context_t;

PyAPI_FUNC(Py_error_t) PyThread_Start(
    PyInterpreter_threading_context_t ctx,
    void (*func)(void *),
    void *arg,
    PyThread_nonlocal_context_t *p_res);
PyAPI_FUNC(Py_error_t) PyThread_Join(PyThread_nonlocal_context_t);


/****************************/
/* interpreter current thread */
/****************************/

struct interp_thread_ctx;
typedef struct interp_thread_ctx * PyInterpreter_thread_context_t;

PyAPI_FUNC(Py_error_t) PyInterpreter_BindThread(
    PyInterpreter_base_context_t *interp,
    PyInterpreter_thread_context_t *p_res);
PyAPI_FUNC(Py_error_t) PyInterpreter_UnbindThread(
    PyInterpreter_thread_context_t);


/****************************/
/* high-level execution */
/****************************/

struct interp_main_ctx;
typedef struct interp_main_ctx * PyInterpreter_main_context_t;

PyAPI_FUNC(Py_error_t) PyInterpreter_SetMain(
    PyThread_base_context_t interp,
    PyInterpreter_main_context_t *p_res);
PyAPI_FUNC(Py_error_t) PyInterpreter_UnsetMain(
    PyInterpreter_main_context_t ctx);

Other API

(expand for code)
/* A high-level brain dump of a possible top-level CPython API.  */


/****************************/
/* common */
/****************************/

//struct error_value {
//    int code;
//    ...
//};
//typedef struct error_value * Py_error_t;
typedef int Py_error_t;

PyAPI_FUNC(void) Py_error_zero(&Py_error_t);
PyAPI_FUNC(int) Py_error_is_zero(Py_error_t);

PyAPI_FUNC(int) Py_GetErrorReturnCode(Py_error_t);
PyAPI_FUNC(void) Py_GetErrorUTF8(Py_error_t, char *buf, size_t bufsize);
//PyAPI_FUNC(void) Py_GetErrorText(Py_error_t, wchar_t *buf, size_t bufsize);
PyAPI_FUNC(int) Py_HandleError(Py_error_t);


/****************************/
/* CPython's main() */
/****************************/

PyAPI_FUNC(int) PyMain_HandleError(Py_error_t);

PyAPI_FUNC(int) Py_Main(PyMain_context_t);


/****************************/
/* global runtime */
/****************************/

// API that takes a PyRuntime_base_context_t.


/****************************/
/* global components */
/****************************/

// API that takes a component-specific context argument.


/****************************/
/* interpreter runtime */
/****************************/

// API that takes a PyInterpreter_base_context_t argument.

// API that takes a PyInterpreter_context_t argument.


/****************************/
/* interpreter components */
/****************************/

// API that takes a component-specific context argument.

/* compiler */

PyAPI_FUNC(Py_error_t) Py_CompileString(
    PyInterpreter_compiler_context_t ctx,
    const char *str,
    const char *filename,
    int start,
    PyObject **p_res);

/* bytecode interpreter */

PyAPI_FUNC(Py_error_t) PyEval_EvalCode(
    PyInterpreter_exec_context_t ctx,
    PyObject *co,
    PyObject *globals,
    PyObject *locals,
    PyObject **p_res);


/****************************/
/* interpreter current thread */
/****************************/

// API that takes a PyThread_context_t argument.


/****************************/
/* high-level execution */
/****************************/

PyAPI_FUNC(Py_error_t) PyRun_SimpleString(
    PyInterpreter_main_context_t ctx,
    const char *command);

// ...

Examples

Rough implementations of the existing C-API:

(expand for code)
extern PyStatus PyStatus_FromError(Py_error_t);


/****************************/
/* Py_Main() */
/****************************/

int
PyLegacy_Main(int argc, wchar_t **argv)
{
}


int
PyLegacy_BytesMain(int argc, char **argv)
{
}


/****************************/
/* Py_Initialize() */
/****************************/

struct interp_contexts {
    PyInterpreter_base_context_t base;
    PyInterpreter_thread_context_t main;
    PyInterpreter_threading_context_t threading;
};

static PyStatus
legacy_interpreter_init(PyRuntime_interpreters_context_t interpreters,
                        PyInterpreter_base_config_t cfg,
                        struct interp_contexts *contexts)
{
    Py_error_t err;

    err = PyInterpreter_Initialize(interpreters, &cfg, &contexts->interp);
    if (!Py_error_is_zero(err)) {
        return PyStatus_FromError(err);
    }

    err = PyInterpreter_BindThread(interp, &contexts->main);
    if (!Py_error_is_zero(err)) {
        (void)PyInterpreter_Finalize(contexts->interp);
        return PyStatus_FromError(err);

    }

    err = PyInterpreter_EnableMultiThreading(interp, &contexts->threading);
    if (!Py_error_is_zero(err)) {
        (void)PyInterpreter_Unbind(contexts->main);
        (void)PyInterpreter_Finalize(contexts->interp);
        return PyStatus_FromError(err);
    }

    return _PyStatus_OK();
}

static PyStatus
legacy_interpreter_fini_components(struct interp_contexts *contexts)
{
    if (!PyInterpreter_threading_context_is_zero(contexts->threading)) {
        (void)PyInterpreter_DisableMultiThreading(threading);
        if (!PyInterpreter_threading_context_is_zero(contexts->main)) {
            assert(PyThread_IsCurrent(contexts->main));
            PyThread_WaitForOtherThreads(threading, contexts->main);
        }
    }

    // pending calls
    // PyAtExit_Call()
    // delete all other thread states

    PyInterpreter_Unbind(contexts->main);

    PyInterpreter_Finalize(contexts->interp);
}


static struct interp_contexts main_interp;

PyStatus
PyLegacy_InitializeFromConfig(const PyConfig *config)
{
    PyStatus status;
    Py_error_t err;

    // Initialize the runtime.
    PyRuntime_base_config_t cfg_runtime = {
        // Derived from config.
    };
    PyRuntime_base_context_t runtime;
    err = PyRuntime_Initialize(&cfg_runtime, &runtime);
    if (!Py_error_is_zero(err)) {
        return PyStatus_FromError(err);
    }

    PyRuntime_interpreters_context_t interpreters;
    err = PyRuntime_EnableSingleInterpreter(runtime, &interpreters);
    if (!Py_error_is_zero(err)) {
    }

    // Initialize the the main interpreter
    // (and leave the main thread context in place).
    PyInterpreter_base_config_t cfg_interp = {
        // ...
    };
    status = legacy_interpreter_init(interpreters, &cfg_interp, &main_interp);
    if (_PyStatus_EXCEPTION(status)) {
        return status;
    }

    // The main thread does special stuff.
    PyRuntime_signals_context_t signals;
    err = PyRuntime_InstallSignalHandlers(runtime, &main_interp.main, &signals);
    if (!Py_error_is_zero(err)) {
        (void)PyRuntime_DisableInterpreters(interpreters);
        (void)legacy_interpreter_fini(&main_interp);
        (void)PyRuntime_Finalize(runtime);
        return PyStatus_FromError(err);
    }

    err = PyRuntime_EnableMultipleInterpreters(interpreters);
    if (!Py_error_is_zero(err)) {
        (void)PyRuntime_UninstallSignalHandlers(signals);
        (void)PyRuntime_DisableInterpreters(interpreters);
        (void)legacy_interpreter_fini(&main_interp);
        (void)PyRuntime_Finalize(runtime);
        return PyStatus_FromError(err);
    }

    // ...

    return _PyStatus_OK();
}


int
PyLegacy_FinalizeEx(void);
{
    int status = 0;
    Py_error_t err;

    PyRuntime_combined_context_t combined;
    PyLegacy_GetRuntimeContext(&combined);
    PyRuntime_base_context_t runtime;
    PyRuntime_GetBaseContext(combined, &runtime);

    if (!PyThread_IsCurrent(main_interp.main)) {
        // XXX error!
        return -1;
    }

    // ...

    PyRuntime_interpreters_context_t interpreters;
    PyRuntime_GetInterpretersContext(combined, &interpreters);
    err = PyRuntime_DisableInterpreters(interpreters);
    if (!Py_error_is_zero(err)) {
        status = -1;
    }
    err = PyRuntime_FinalizeOtherInterpreters(interpreters, main_interp.base);
    //err = legacy_finalize_other_interpreters(interpreters, main_interp.base);
    if (!Py_error_is_zero(err)) {
        status = -1;
    }

    PyRuntime_signals_context_t signals;
    PyRuntime_GetSignalsContext(combined, &signals);
    err = PyRuntime_UninstallSignalHandlers(signals);
    if (!Py_error_is_zero(err)) {
        status = -1;
    }

    err = legacy_interpreter_fini(&main_interp);
    if (!Py_error_is_zero(err)) {
        status = -1;
    }

    err = PyRuntimeFinalize(runtime);
    if (!Py_error_is_zero(err)) {
        status = -1;
    }

    return status;
}

Fresh examples:

(expand for code)
extern int decode_argv(const int argc, const char **argv, const char *encoding,
                       wchar_t **p_res);


static int
main1(int argc, char **argv)
{
    PyMain_args_t args = {
        .argc = argc,
        .argv = NULL,
        .config = {
            // ...
        },
    }
    if (decode_argv(argc, argv, &args.argv, "utf-8") < 0) {
        return 1;
    }

    PyMain_context_t ctx;
    Py_error_t err = PyMain_Initialize(&cfg, &ctx);
    int rc = PyMain_HandleError(err);
    if (rc != 0) {
        return rc;
    }

    rc = Py_Main(ctx);
    err = PyMain_Finalize(ctx);
    (void)PyMain_HandleError(err);
    return rc;
}

static int
main2(int argc, char **argv)
{
    int rc;
    Py_error_t err;
    Py_error_zero(err);

    PyRuntime_base_context_t runtime;
    PyRuntime_signals_context_t signals1;
    PyInterpreter_base_context_t interp;
    PyInterpreter_thread_context_t main;
    PyRuntime_signals_context_t signals2;
    PyInterpreter_threading_context_t threading;
    PyThread_nonlocal_context_t thread;

    PyRuntime_base_context_zero(&runtime);
    PyRuntime_signals_context_zero(&signals1);
    PyInterpreter_base_context_zero(&interp);
    PyInterpreter_thread_context_zero(&main);
    PyRuntime_signals_context_zero(&signals2);
    PyInterpreter_threading_context_zero(&threading);
    PyThread_nonlocal_context_zero(&thread);

    PyRuntime_base_config_t cfg_runtime = {
        // ...
    };
    err = PyRuntime_Initialize(&cfg_runtime, &runtime);
    // ...or that could be named Py_Initialize()...
    rc = Py_HandleError(err);
    if (rc != 0) {
        goto finally;
    }

    /* At this point we can use the runtime without an interpreter. */

    err = PyRuntime_InstallSignalHandlers(runtime, NULL, &signals1);
    // ...or that could be named PyMain_InstallSignalHandlers()...
    rc = Py_HandleError(err);
    if (rc != 0) {
        goto finally;
    }

    PyInterpreter_base_config_t cfg_interp = {
        // ...
    };
    err = PyInterpreter_Initialize(runtime, &cfg_interp, &interp);
    // ...or that could be named Py_NewInterpreter()...
    rc = Py_HandleError(err);
    if (rc != 0) {
        goto finally;
    }

    err = PyInterpreter_BindThread(interp, &main);
    rc = Py_HandleError(err);
    if (rc != 0) {
        goto finally;
    }

    if (PyRuntime_signals_is_zero(signals1)) {
        err = PyRuntime_InstallSignalHandlers(runtime, &main, &signals2);
        rc = Py_HandleError(err);
        if (rc != 0) {
            goto finally;
        }
    }

    // Start a subthread.

    err = PyInterpreter_EnableMultiThreading(interp, &threading);
    rc = Py_HandleError(err);
    if (rc != 0) {
        goto finally;
    }

    err = PyThread_Start(func, arg, &thread);
    rc = Py_HandleError(err);
    if (rc != 0) {
        goto finally;
    }
    /* Manage the thread using the "non-local" handle. */

    /* Do more stuff in the current thread. */
    // ...
    rc = 0;

finally:
    if (!PyThread_nonlocal_context_is_zero(thread)) {
        err = PyInterpreter_Unbind(main);
        int rc2 = Py_HandleError(err);
        rc = rc == 0 ? rc2 : rc;
    }
    if (!PyInterpreter_thread_context_is_zero(main)) {
        err = PyInterpreter_Unbind(main);
        int rc2 = Py_HandleError(err);
        rc = rc == 0 ? rc2 : rc;
    }
    if (!PyInterpreter_threading_context_is_zero(threading)) {
        err = PyInterpreter_DisableMultiThreading(threading);
        int rc2 = Py_HandleError(err);
        rc = rc == 0 ? rc2 : rc;
    }
    if (!PyInterpreter_signals_context_is_zero(signals2)) {
        err = PyInterpreter_UninstallSignalHandlers(signals2);
        int rc2 = Py_HandleError(err);
        rc = rc == 0 ? rc2 : rc;
    }
    if (!PyInterpreter_base_context_is_zero(interp)) {
        err = PyInterpreter_Finalize(interp);
        // ...or that could be named Py_EndInterpreter()...
        int rc2 = Py_HandleError(err);
        rc = rc == 0 ? rc2 : rc;
    }
    if (!PyRuntime_signals_context_is_zero(signals1)) {
        err = PyRuntime_UninstallSignalHandlers(signals1);
        int rc2 = Py_HandleError(err);
        rc = rc == 0 ? rc2 : rc;
    }
    if (!PyRuntime_base_context_is_zero(runtime)) {
        err = PyRuntime_Finalize(runtime);
        // ...or that could be named Py_Finalize()...
        int rc2 = Py_HandleError(err);
        rc = rc == 0 ? rc2 : rc;
    }
    return rc;
}

static int
main3(int argc, char **argv)
{
}

int
main(int argc, char **argv)
{
    return main1(argc, argv);
    //return main2(argc, argv);
    //return main3(argc, argv);
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment