I’m currently elbow deep in landing the patches for iOS support, and think I’ve hit an edge case in either the definition or implementation of PEP 734 (the new subinterpreters PEP).
When a Python process starts, it creates a single interpreter state (the “main” interpreter) with a single thread state for the current OS thread. The Python runtime is then initialized using them.
That makes sense at a high level; however in practice it’s unclear to me how/where this initialization is meant to occur.
The _PyInterpreterState_SetRunningMain()
method that sets up the main subinterpreter is currently invoked in pymain_run_python()
. If you’re using a standard CPython interpreter, this works great - the main interpreter state is set just before the user’s code is imported and executed, and cleaned up afterwards.
However, an iOS app doesn’t use pymain_run_python()
- as there’s no command line experience, an iOS app is effectively a custom executable that embeds a C Python interpreter using the C API.
As a result, when I run the test__xxsubinterpreters
test case on iOS, all the tests pass, except one - IsRunningTests.test_main()
- because the main subinterpreter state hasn’t been initialized. The problem is that unless I’m missing something, there there’s no public API to do this. There’s a public API to initialize new subinterpreters - but not the main subinterpreter.
Although I’m hitting this as part of the iOS work, I believe the same problem will exist with any embedded CPython usage. Any of the example code in the CPython embedding guide would report that the main subinterpreter is uninitialised.
It seems to me that either:
_PyInterpreterState_SetRunningMain()
should be invoked as a side effect of callingPy_Initialize()
/Py_InitializeFromConfig()
, and cleared inPy_Finalize()
- There’s a missing C API endpoint to initialize the main interpreter.
- An iOS app should be calling private APIs to initialize the main interpreter (essentially treating the iOS app as an alternate
Py_BUILD_CORE
target) - An embedded CPython interpreter is a special case that doesn’t have a main subinterpreter for some reason.
My best guess is that (1) is the most appropriate response - but I’m not sure if that refactoring would have other unintended consequences (or, for that matter, where in the interpreter initialisation process would be appropriate).
Have I missed something obvious here? If (1) is the right approach, any tips on where the initialisation should occur? I’m happy to work up a patch - I just need to be pointed in the right direction.