GV

GeezerVison

The World Wide Web, restored to 800 pixels of glory.

Home|About|Gaming cabinet|Archives|How it works|Starter sites

On this site

  • Sign in
  • ActionsCI/CD and automation
  • CodespacesCloud development environments
  • All Features
  • Documentation
  • Training
  • Certifications
  • Build with Copilot
  • Customize Copilot
  • Integrations
  • Trending repos
  • GitHub Sponsors
  • Developer articles
  • Enterprises

This view

  • Original page
  • New address

GeezerVison is showing github.com — gh-85283: Add PyInterpreterState_IsMain() function by vstinner · Pull Request #108577 · python/cpython

Conversation

@vstinner

@vstinner vstinner commented Aug 28, 2023 •
edited by github-actions Bot
Loading

Uh oh!

There was an error while loading. Please reload this page.

Copy link
Copy Markdown
Member

This function can be used to convert the syslog extension to the limited C API. The PyInterpreterState_Main() is not available in the limited C API.

  • Issue: [C API] Convert a few stdlib extensions to the limited C API (PEP 384) #85283

📚 Documentation preview 📚: https://cpython-previews--108577.org.readthedocs.build/

Sorry, something went wrong.

Uh oh!

There was an error while loading. Please reload this page.

sidba2003 reacted with heart emoji
All reactions
  • ❤️ 1 reaction
@vstinner
vstinner requested a review from encukou as a code owner August 28, 2023 17:09
@bedevere-bot bedevere-bot mentioned this pull request Aug 28, 2023
[C API] Convert a few stdlib extensions to the limited C API (PEP 384) #85283
Closed
@bedevere-bot bedevere-bot added the awaiting core review label Aug 28, 2023
@vstinner

vstinner commented Aug 28, 2023

Copy link
Copy Markdown
Member Author

cc @ericsnowcurrently

Maybe the concept of "main interpreter" should be elaborate in the doc, since right now it's simply not documented or defined. I suppose that it's the only one which is allowed to register signal handlers?

Currently, the main interpreter has more features:

  • Can fork()
  • Has the "obmalloc" state (pymalloc)
  • Is responsible for init/clean global Python objects and other "global Python states", like static types and static built-ins, interned Unicode strings, GIL State, etc.
  • Can use PyOS_Readline()
  • Can import extensions which don't support sub-interpreters
  • Can call _PyPathConfig_UpdateGlobal()
  • Initialize _PyRuntime

I'm not sure about exec() and threads(), and these options:

    if (config->use_main_obmalloc) {
        interp->feature_flags |= Py_RTFLAGS_USE_MAIN_OBMALLOC;
    }
    else if (!config->check_multi_interp_extensions) {
        /* The reason: PyModuleDef.m_base.m_copy leaks objects between
           interpreters. */
        return _PyStatus_ERR("per-interpreter obmalloc does not support "
                             "single-phase init extension modules");
    }

    if (config->allow_fork) {
        interp->feature_flags |= Py_RTFLAGS_FORK;
    }
    if (config->allow_exec) {
        interp->feature_flags |= Py_RTFLAGS_EXEC;
    }
    // Note that fork+exec is always allowed.

    if (config->allow_threads) {
        interp->feature_flags |= Py_RTFLAGS_THREADS;
    }
    if (config->allow_daemon_threads) {
        interp->feature_flags |= Py_RTFLAGS_DAEMON_THREADS;
    }

    if (config->check_multi_interp_extensions) {
        interp->feature_flags |= Py_RTFLAGS_MULTI_INTERP_EXTENSIONS;
    }

The alternative is to expose PyInterpreterState_Main() which gives the main interpreter. I'm not sure which API is the best.

In Python 3.13, I also added Py_IsFinalizing(): https://docs.python.org/dev/c-api/init.html#c.Py_IsFinalizing

sidba2003 reacted with heart emoji
All reactions
  • ❤️ 1 reaction

Sorry, something went wrong.

Uh oh!

There was an error while loading. Please reload this page.

@vstinner
pythongh-85283: Add PyInterpreterState_IsMain() function
dac62e2
This function can be used to convert the syslog extension to the
limited C API. The PyInterpreterState_Main() is not available in the
limited C API.
@vstinner
vstinner force-pushed the interp_is_main branch from 9e81a3b to dac62e2 Compare August 28, 2023 17:24
@vstinner
vstinner requested a review from a team as a code owner August 28, 2023 17:24
@ericsnowcurrently

ericsnowcurrently commented Aug 29, 2023

Copy link
Copy Markdown
Member

Maybe the concept of "main interpreter" should be elaborate in the doc, since right now it's simply not documented or defined.

See https://docs.python.org/3.12/c-api/init.html#sub-interpreter-support.

The alternative is to expose PyInterpreterState_Main() which gives the main interpreter. I'm not sure which API is the best.

Having both would be fine.

All reactions

Sorry, something went wrong.

Uh oh!

There was an error while loading. Please reload this page.

@vstinner

vstinner commented Aug 29, 2023

Copy link
Copy Markdown
Member Author

I would prefer to only add a single function to the limited C API.

All reactions

Sorry, something went wrong.

Uh oh!

There was an error while loading. Please reload this page.

@encukou

encukou commented Aug 31, 2023 •
edited
Loading

Uh oh!

There was an error while loading. Please reload this page.

Copy link
Copy Markdown
Member

Long-term, I don't think relying on “main”-ness is the right way.
In syslog, I guess only one interpreter can open the log, but it doesn't have to be the main interpreter.

All reactions

Sorry, something went wrong.

Uh oh!

There was an error while loading. Please reload this page.

@vstinner

vstinner commented Aug 31, 2023

Copy link
Copy Markdown
Member Author

Long-term, I don't think relying on “main”-ness is the right way.
In syslog, I guess only one interpreter can open the log, but it doesn't have to be the main interpreter.

For the specific case of the syslog module, please see issue gh-99127 and commit 8bb2303.

All reactions

Sorry, something went wrong.

Uh oh!

There was an error while loading. Please reload this page.

@encukou

encukou commented Aug 31, 2023

Copy link
Copy Markdown
Member

Yes. The current code makes an assumption that the “app” (that is, top-level code that sets up logging) lives in the main interpreter.
That's a fairly reasonable assumption at the moment, when the focus is on getting multiple interpreters working. But I'd rather not encourage relying on this assumption in the long term.

erlend-aasland reacted with thumbs up emoji
All reactions
  • 👍 1 reaction

Sorry, something went wrong.

Uh oh!

There was an error while loading. Please reload this page.

@vstinner

vstinner commented Sep 6, 2023

Copy link
Copy Markdown
Member Author

Yes. The current code makes an assumption that the “app” (that is, top-level code that sets up logging) lives in the main interpreter. That's a fairly reasonable assumption at the moment, when the focus is on getting multiple interpreters working. But I'd rather not encourage relying on this assumption in the long term.

In Python, the main interpreter is special. It has special skills and using Python API behaves differently depending if the current interpreter is the main one or not. So IMO it makes sense to check if the current code is running the main interpreter or not.

I suggest to leave the syslog extension aside. It can make its own decisions and yes, it can change in the future :-)


Cython uses a differerent logic to handle PEP 489 "Multi-phase extension module initialization". It uses a static PY_INT64_T main_interpreter_id = -1; variable which is initialized at the first call of __Pyx_check_single_interpreter().

But the problem is unrelated to checking if we are running the main interpreter or not.

//#if CYTHON_PEP489_MULTI_PHASE_INIT
static CYTHON_SMALL_CODE int __Pyx_check_single_interpreter(void) {
    #if PY_VERSION_HEX >= 0x030700A1
    static PY_INT64_T main_interpreter_id = -1;
    PY_INT64_T current_id = PyInterpreterState_GetID(PyThreadState_Get()->interp);
    if (main_interpreter_id == -1) {
        main_interpreter_id = current_id;
        return (unlikely(current_id == -1)) ? -1 : 0;
    } else if (unlikely(main_interpreter_id != current_id))

    #else
    static PyInterpreterState *main_interpreter = NULL;
    PyInterpreterState *current_interpreter = PyThreadState_Get()->interp;
    if (!main_interpreter) {
        main_interpreter = current_interpreter;
    } else if (unlikely(main_interpreter != current_interpreter))
    #endif

    {
        PyErr_SetString(
            PyExc_ImportError,
            "Interpreter change detected - this module can only be loaded into one interpreter per process.");
        return -1;
    }
    return 0;
}
All reactions

Sorry, something went wrong.

Uh oh!

There was an error while loading. Please reload this page.

@vstinner

vstinner commented Sep 6, 2023

Copy link
Copy Markdown
Member Author

Usage of the different existing APIs to check for the main intrepreter in PyPI top 5,000 projects.

Affected projects (9):

  • billiard (4.1.0)
  • catboost (1.2)
  • fastobo (0.12.2)
  • multiprocess (0.70.14)
  • numpy (1.25.0)
  • orjson (3.9.1)
  • psycopg2 (2.9.6)
  • psycopg2-binary (2.9.6)
  • tensorstore (0.1.40)

PyO3 binding has a comment about the lack of limited C API for this:

pyo3-ffi/src/intrcheck.rs: // skipped non-limited _PyOS_IsMainThread

tensorstore also has a comment about _PyOS_IsMainThread():

/// We can't use `_PyOS_IsMainThread()` because that requires the GIL on some

All matches:

PYPI-2023-07-04/billiard-4.1.0.tar.gz: billiard-4.1.0/Modules/_billiard/win32_functions.c: if (!wait_flag && _PyOS_IsMainThread()) {
PYPI-2023-07-04/fastobo-0.12.2.tar.gz: fastobo-0.12.2/crates/pyo3-ffi/src/cpython/pystate.rs: pub fn PyInterpreterState_Main() -> *mut PyInterpreterState;
PYPI-2023-07-04/fastobo-0.12.2.tar.gz: fastobo-0.12.2/crates/pyo3-ffi/src/intrcheck.rs: // skipped non-limited _PyOS_IsMainThread
PYPI-2023-07-04/catboost-1.2.tar.gz: catboost-1.2/catboost_all_src/contrib/python/numpy/py3/numpy/core/src/multiarray/multiarraymodule.c: if (PyThreadState_Get()->interp != PyInterpreterState_Main()) {
PYPI-2023-07-04/numpy-1.25.0.tar.gz: numpy-1.25.0/numpy/core/src/multiarray/multiarraymodule.c: if (PyThreadState_Get()->interp != PyInterpreterState_Main()) {
PYPI-2023-07-04/orjson-3.9.1.tar.gz: orjson-3.9.1/include/cargo/pyo3-ffi-0.19.0/src/cpython/pystate.rs: pub fn PyInterpreterState_Main() -> *mut PyInterpreterState;
PYPI-2023-07-04/orjson-3.9.1.tar.gz: orjson-3.9.1/include/cargo/pyo3-ffi-0.19.0/src/intrcheck.rs: // skipped non-limited _PyOS_IsMainThread
PYPI-2023-07-04/psycopg2-2.9.6.tar.gz: psycopg2-2.9.6/psycopg/utils.c: return _PyInterpreterState_Get() == PyInterpreterState_Main();
PYPI-2023-07-04/psycopg2-binary-2.9.6.tar.gz: psycopg2-binary-2.9.6/psycopg/utils.c: return _PyInterpreterState_Get() == PyInterpreterState_Main();
PYPI-2023-07-04/multiprocess-0.70.14.tar.gz: multiprocess-0.70.14/py3.10/Modules/_multiprocess/semaphore.c: if (_PyOS_IsMainThread()) {
PYPI-2023-07-04/multiprocess-0.70.14.tar.gz: multiprocess-0.70.14/py3.11/Modules/_multiprocess/semaphore.c: if (_PyOS_IsMainThread()) {
PYPI-2023-07-04/multiprocess-0.70.14.tar.gz: multiprocess-0.70.14/py3.7/Modules/_multiprocess/semaphore.c: if (_PyOS_IsMainThread()) {
PYPI-2023-07-04/multiprocess-0.70.14.tar.gz: multiprocess-0.70.14/py3.8/Modules/_multiprocess/semaphore.c: if (_PyOS_IsMainThread()) {
PYPI-2023-07-04/multiprocess-0.70.14.tar.gz: multiprocess-0.70.14/py3.9/Modules/_multiprocess/semaphore.c: if (_PyOS_IsMainThread()) {
PYPI-2023-07-04/tensorstore-0.1.40.tar.gz: tensorstore-0.1.40/python/tensorstore/gil_safe.cc: /// We can't use `_PyOS_IsMainThread()` because that requires the GIL on some
All reactions

Sorry, something went wrong.

Uh oh!

There was an error while loading. Please reload this page.

@encukou

encukou commented Sep 7, 2023

Copy link
Copy Markdown
Member

Cython uses a differerent logic to handle PEP 489 "Multi-phase extension module initialization". It uses a static PY_INT64_T main_interpreter_id = -1; variable which is initialized at the first call of __Pyx_check_single_interpreter().

But the problem is unrelated to checking if we are running the main interpreter or not.

IMO, this is along the lines of what syslog should do. It doesn't need the special skills of the main interpreter -- it only requires that different interpreters don't interfere with each other.
(Also, syslog.closelog can reset that int back to -1.)

All reactions

Sorry, something went wrong.

Uh oh!

There was an error while loading. Please reload this page.

@vstinner

vstinner commented Sep 13, 2023

Copy link
Copy Markdown
Member Author

IMO, this is along the lines of what syslog should do. It doesn't need the special skills of the main interpreter -- it only requires that different interpreters don't interfere with each other.

Do you want to propose an API to implement such check? How would the API look like?

All reactions

Sorry, something went wrong.

Uh oh!

There was an error while loading. Please reload this page.

@vstinner

vstinner commented Oct 3, 2023

Copy link
Copy Markdown
Member Author

Using the limited C API in stdlib C extensions is somehow on hold, and it became unclear to me if this function is the right want to handle sub-interpreters: see issue gh-109857. So I prefer close this PR for now.

All reactions

Sorry, something went wrong.

Uh oh!

There was an error while loading. Please reload this page.

@vstinner vstinner closed this Oct 3, 2023
@vstinner
vstinner deleted the interp_is_main branch October 3, 2023 16:27
@vstinner vstinner mentioned this pull request Oct 3, 2023
(Mostly) Stop Special-casing the Main Interpreter #109857
Open
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Reviewers

@encukou encukou Awaiting requested review from encukou encukou is a code owner

Uh oh!

There was an error while loading. Please reload this page.

Assignees

No one assigned

Labels

awaiting core review

Projects

None yet

Milestone

No milestone

Development

Successfully merging this pull request may close these issues.

Uh oh!

There was an error while loading. Please reload this page.

4 participants

@vstinner @ericsnowcurrently @encukou @bedevere-bot
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
You are visitor 01402503 — GeezerVison does not store the pages you look up.
Best viewed with Netscape Navigator 4.7 or Internet Explorer 6 at 800×600. No cookies required.