Skip to content

Commit be3b295

Browse files
bpo-35886: Make PyInterpreterState an opaque type in the public API. (GH-11731)
Move PyInterpreterState into the "internal" header files.
1 parent 175421b commit be3b295

File tree

8 files changed

+129
-102
lines changed

8 files changed

+129
-102
lines changed

Doc/whatsnew/3.8.rst

+10
Original file line numberDiff line numberDiff line change
@@ -571,6 +571,16 @@ Changes in the Python API
571571
* ``PyGC_Head`` struct is changed completely. All code touched the
572572
struct member should be rewritten. (See :issue:`33597`)
573573

574+
* The ``PyInterpreterState`` struct has been moved into the "internal"
575+
header files (specifically Include/internal/pycore_pystate.h). An
576+
opaque ``PyInterpreterState`` is still available as part of the public
577+
API (and stable ABI). The docs indicate that none of the struct's
578+
fields are public, so we hope no one has been using them. However,
579+
if you do rely on one or more of those private fields and have no
580+
alternative then please open a BPO issue. We'll work on helping
581+
you adjust (possibly including adding accessor functions to the
582+
public API). (See :issue:`35886`.)
583+
574584
* Asyncio tasks can now be named, either by passing the ``name`` keyword
575585
argument to :func:`asyncio.create_task` or
576586
the :meth:`~asyncio.loop.create_task` event loop method, or by

Include/cpython/pystate.h

+5-60
Original file line numberDiff line numberDiff line change
@@ -6,8 +6,6 @@
66
extern "C" {
77
#endif
88

9-
typedef PyObject* (*_PyFrameEvalFunction)(struct _frame *, int);
10-
119
/* Placeholders while working on the new configuration API
1210
*
1311
* See PEP 432 for final anticipated contents
@@ -30,63 +28,9 @@ typedef struct {
3028
(_PyMainInterpreterConfig){.install_signal_handlers = -1}
3129
/* Note: _PyMainInterpreterConfig_INIT sets other fields to 0/NULL */
3230

33-
typedef struct _is {
34-
35-
struct _is *next;
36-
struct _ts *tstate_head;
37-
38-
int64_t id;
39-
int64_t id_refcount;
40-
PyThread_type_lock id_mutex;
41-
42-
PyObject *modules;
43-
PyObject *modules_by_index;
44-
PyObject *sysdict;
45-
PyObject *builtins;
46-
PyObject *importlib;
47-
48-
/* Used in Python/sysmodule.c. */
49-
int check_interval;
50-
51-
/* Used in Modules/_threadmodule.c. */
52-
long num_threads;
53-
/* Support for runtime thread stack size tuning.
54-
A value of 0 means using the platform's default stack size
55-
or the size specified by the THREAD_STACK_SIZE macro. */
56-
/* Used in Python/thread.c. */
57-
size_t pythread_stacksize;
58-
59-
PyObject *codec_search_path;
60-
PyObject *codec_search_cache;
61-
PyObject *codec_error_registry;
62-
int codecs_initialized;
63-
int fscodec_initialized;
64-
65-
_PyCoreConfig core_config;
66-
_PyMainInterpreterConfig config;
67-
#ifdef HAVE_DLOPEN
68-
int dlopenflags;
69-
#endif
70-
71-
PyObject *builtins_copy;
72-
PyObject *import_func;
73-
/* Initialized to PyEval_EvalFrameDefault(). */
74-
_PyFrameEvalFunction eval_frame;
75-
76-
Py_ssize_t co_extra_user_count;
77-
freefunc co_extra_freefuncs[MAX_CO_EXTRA_USERS];
78-
79-
#ifdef HAVE_FORK
80-
PyObject *before_forkers;
81-
PyObject *after_forkers_parent;
82-
PyObject *after_forkers_child;
83-
#endif
84-
/* AtExit module */
85-
void (*pyexitfunc)(PyObject *);
86-
PyObject *pyexitmodule;
31+
PyAPI_FUNC(_PyCoreConfig *) _PyInterpreterState_GetCoreConfig(PyInterpreterState *);
32+
PyAPI_FUNC(_PyMainInterpreterConfig *) _PyInterpreterState_GetMainConfig(PyInterpreterState *);
8733

88-
uint64_t tstate_next_unique_id;
89-
} PyInterpreterState;
9034

9135
/* State unique per thread */
9236

@@ -122,7 +66,8 @@ typedef struct _err_stackitem {
12266
} _PyErr_StackItem;
12367

12468

125-
typedef struct _ts {
69+
// The PyThreadState typedef is in Include/pystate.h.
70+
struct _ts {
12671
/* See Python/ceval.c for comments explaining most fields */
12772

12873
struct _ts *prev;
@@ -214,7 +159,7 @@ typedef struct _ts {
214159

215160
/* XXX signal handlers should also be here */
216161

217-
} PyThreadState;
162+
};
218163

219164
/* Get the current interpreter state.
220165

Include/internal/pycore_pystate.h

+85-24
Original file line numberDiff line numberDiff line change
@@ -17,37 +17,74 @@ extern "C" {
1717
#include "pycore_warnings.h"
1818

1919

20-
/* GIL state */
20+
/* interpreter state */
2121

22-
struct _gilstate_runtime_state {
23-
int check_enabled;
24-
/* Assuming the current thread holds the GIL, this is the
25-
PyThreadState for the current thread. */
26-
_Py_atomic_address tstate_current;
27-
PyThreadFrameGetter getframe;
28-
/* The single PyInterpreterState used by this process'
29-
GILState implementation
30-
*/
31-
/* TODO: Given interp_main, it may be possible to kill this ref */
32-
PyInterpreterState *autoInterpreterState;
33-
Py_tss_t autoTSSkey;
34-
};
22+
typedef PyObject* (*_PyFrameEvalFunction)(struct _frame *, int);
23+
24+
// The PyInterpreterState typedef is in Include/pystate.h.
25+
struct _is {
26+
27+
struct _is *next;
28+
struct _ts *tstate_head;
29+
30+
int64_t id;
31+
int64_t id_refcount;
32+
PyThread_type_lock id_mutex;
33+
34+
PyObject *modules;
35+
PyObject *modules_by_index;
36+
PyObject *sysdict;
37+
PyObject *builtins;
38+
PyObject *importlib;
39+
40+
/* Used in Python/sysmodule.c. */
41+
int check_interval;
42+
43+
/* Used in Modules/_threadmodule.c. */
44+
long num_threads;
45+
/* Support for runtime thread stack size tuning.
46+
A value of 0 means using the platform's default stack size
47+
or the size specified by the THREAD_STACK_SIZE macro. */
48+
/* Used in Python/thread.c. */
49+
size_t pythread_stacksize;
50+
51+
PyObject *codec_search_path;
52+
PyObject *codec_search_cache;
53+
PyObject *codec_error_registry;
54+
int codecs_initialized;
55+
int fscodec_initialized;
56+
57+
_PyCoreConfig core_config;
58+
_PyMainInterpreterConfig config;
59+
#ifdef HAVE_DLOPEN
60+
int dlopenflags;
61+
#endif
3562

36-
/* hook for PyEval_GetFrame(), requested for Psyco */
37-
#define _PyThreadState_GetFrame _PyRuntime.gilstate.getframe
63+
PyObject *builtins_copy;
64+
PyObject *import_func;
65+
/* Initialized to PyEval_EvalFrameDefault(). */
66+
_PyFrameEvalFunction eval_frame;
3867

39-
/* Issue #26558: Flag to disable PyGILState_Check().
40-
If set to non-zero, PyGILState_Check() always return 1. */
41-
#define _PyGILState_check_enabled _PyRuntime.gilstate.check_enabled
68+
Py_ssize_t co_extra_user_count;
69+
freefunc co_extra_freefuncs[MAX_CO_EXTRA_USERS];
4270

71+
#ifdef HAVE_FORK
72+
PyObject *before_forkers;
73+
PyObject *after_forkers_parent;
74+
PyObject *after_forkers_child;
75+
#endif
76+
/* AtExit module */
77+
void (*pyexitfunc)(PyObject *);
78+
PyObject *pyexitmodule;
4379

44-
/* interpreter state */
80+
uint64_t tstate_next_unique_id;
81+
};
4582

46-
PyAPI_FUNC(PyInterpreterState *) _PyInterpreterState_LookUpID(PY_INT64_T);
83+
PyAPI_FUNC(struct _is*) _PyInterpreterState_LookUpID(PY_INT64_T);
4784

48-
PyAPI_FUNC(int) _PyInterpreterState_IDInitref(PyInterpreterState *);
49-
PyAPI_FUNC(void) _PyInterpreterState_IDIncref(PyInterpreterState *);
50-
PyAPI_FUNC(void) _PyInterpreterState_IDDecref(PyInterpreterState *);
85+
PyAPI_FUNC(int) _PyInterpreterState_IDInitref(struct _is *);
86+
PyAPI_FUNC(void) _PyInterpreterState_IDIncref(struct _is *);
87+
PyAPI_FUNC(void) _PyInterpreterState_IDDecref(struct _is *);
5188

5289

5390
/* cross-interpreter data */
@@ -119,6 +156,30 @@ struct _xidregitem {
119156
};
120157

121158

159+
/* GIL state */
160+
161+
struct _gilstate_runtime_state {
162+
int check_enabled;
163+
/* Assuming the current thread holds the GIL, this is the
164+
PyThreadState for the current thread. */
165+
_Py_atomic_address tstate_current;
166+
PyThreadFrameGetter getframe;
167+
/* The single PyInterpreterState used by this process'
168+
GILState implementation
169+
*/
170+
/* TODO: Given interp_main, it may be possible to kill this ref */
171+
PyInterpreterState *autoInterpreterState;
172+
Py_tss_t autoTSSkey;
173+
};
174+
175+
/* hook for PyEval_GetFrame(), requested for Psyco */
176+
#define _PyThreadState_GetFrame _PyRuntime.gilstate.getframe
177+
178+
/* Issue #26558: Flag to disable PyGILState_Check().
179+
If set to non-zero, PyGILState_Check() always return 1. */
180+
#define _PyGILState_check_enabled _PyRuntime.gilstate.check_enabled
181+
182+
122183
/* Full Python runtime state */
123184

124185
typedef struct pyruntimestate {

Include/pystate.h

+12-14
Original file line numberDiff line numberDiff line change
@@ -20,22 +20,20 @@ struct _frame;
2020
struct _ts;
2121
struct _is;
2222

23-
#ifdef Py_LIMITED_API
23+
/* struct _ts is defined in cpython/pystate.h */
2424
typedef struct _ts PyThreadState;
25+
/* struct _is is defined in internal/pycore_pystate.h */
2526
typedef struct _is PyInterpreterState;
26-
#else
27-
/* PyThreadState and PyInterpreterState are defined in cpython/pystate.h */
28-
#endif
2927

3028
/* State unique per thread */
3129

32-
PyAPI_FUNC(struct _is *) PyInterpreterState_New(void);
33-
PyAPI_FUNC(void) PyInterpreterState_Clear(struct _is *);
34-
PyAPI_FUNC(void) PyInterpreterState_Delete(struct _is *);
30+
PyAPI_FUNC(PyInterpreterState *) PyInterpreterState_New(void);
31+
PyAPI_FUNC(void) PyInterpreterState_Clear(PyInterpreterState *);
32+
PyAPI_FUNC(void) PyInterpreterState_Delete(PyInterpreterState *);
3533

3634
#if !defined(Py_LIMITED_API) || Py_LIMITED_API+0 >= 0x03070000
3735
/* New in 3.7 */
38-
PyAPI_FUNC(int64_t) PyInterpreterState_GetID(struct _is *);
36+
PyAPI_FUNC(int64_t) PyInterpreterState_GetID(PyInterpreterState *);
3937
#endif
4038
#if !defined(Py_LIMITED_API) || Py_LIMITED_API+0 >= 0x03030000
4139
/* New in 3.3 */
@@ -44,9 +42,9 @@ PyAPI_FUNC(int) PyState_RemoveModule(struct PyModuleDef*);
4442
#endif
4543
PyAPI_FUNC(PyObject*) PyState_FindModule(struct PyModuleDef*);
4644

47-
PyAPI_FUNC(struct _ts *) PyThreadState_New(struct _is *);
48-
PyAPI_FUNC(void) PyThreadState_Clear(struct _ts *);
49-
PyAPI_FUNC(void) PyThreadState_Delete(struct _ts *);
45+
PyAPI_FUNC(PyThreadState *) PyThreadState_New(PyInterpreterState *);
46+
PyAPI_FUNC(void) PyThreadState_Clear(PyThreadState *);
47+
PyAPI_FUNC(void) PyThreadState_Delete(PyThreadState *);
5048
PyAPI_FUNC(void) PyThreadState_DeleteCurrent(void);
5149

5250
/* Get the current thread state.
@@ -57,7 +55,7 @@ PyAPI_FUNC(void) PyThreadState_DeleteCurrent(void);
5755
The caller must hold the GIL.
5856
5957
See also PyThreadState_GET() and _PyThreadState_GET(). */
60-
PyAPI_FUNC(struct _ts *) PyThreadState_Get(void);
58+
PyAPI_FUNC(PyThreadState *) PyThreadState_Get(void);
6159

6260
/* Get the current Python thread state.
6361
@@ -70,7 +68,7 @@ PyAPI_FUNC(struct _ts *) PyThreadState_Get(void);
7068
See also PyThreadState_Get() and _PyThreadState_GET(). */
7169
#define PyThreadState_GET() PyThreadState_Get()
7270

73-
PyAPI_FUNC(struct _ts *) PyThreadState_Swap(struct _ts *);
71+
PyAPI_FUNC(PyThreadState *) PyThreadState_Swap(PyThreadState *);
7472
PyAPI_FUNC(PyObject *) PyThreadState_GetDict(void);
7573
PyAPI_FUNC(int) PyThreadState_SetAsyncExc(unsigned long, PyObject *);
7674

@@ -118,7 +116,7 @@ PyAPI_FUNC(void) PyGILState_Release(PyGILState_STATE);
118116
thread-state, even if no auto-thread-state call has been made
119117
on the main thread.
120118
*/
121-
PyAPI_FUNC(struct _ts *) PyGILState_GetThisThreadState(void);
119+
PyAPI_FUNC(PyThreadState *) PyGILState_GetThisThreadState(void);
122120

123121

124122
#ifndef Py_LIMITED_API
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,2 @@
1+
The implementation of PyInterpreterState has been moved into the internal
2+
header files (guarded by Py_BUILD_CORE).

Modules/_testcapimodule.c

+2-2
Original file line numberDiff line numberDiff line change
@@ -4685,7 +4685,7 @@ static PyObject *
46854685
get_core_config(PyObject *self, PyObject *Py_UNUSED(args))
46864686
{
46874687
PyInterpreterState *interp = _PyInterpreterState_Get();
4688-
const _PyCoreConfig *config = &interp->core_config;
4688+
const _PyCoreConfig *config = _PyInterpreterState_GetCoreConfig(interp);
46894689
return _PyCoreConfig_AsDict(config);
46904690
}
46914691

@@ -4694,7 +4694,7 @@ static PyObject *
46944694
get_main_config(PyObject *self, PyObject *Py_UNUSED(args))
46954695
{
46964696
PyInterpreterState *interp = _PyInterpreterState_Get();
4697-
const _PyMainInterpreterConfig *config = &interp->config;
4697+
const _PyMainInterpreterConfig *config = _PyInterpreterState_GetMainConfig(interp);
46984698
return _PyMainInterpreterConfig_AsDict(config);
46994699
}
47004700

Programs/_testembed.c

+2-2
Original file line numberDiff line numberDiff line change
@@ -315,7 +315,7 @@ dump_config_impl(void)
315315

316316
/* core config */
317317
PyInterpreterState *interp = _PyInterpreterState_Get();
318-
const _PyCoreConfig *core_config = &interp->core_config;
318+
const _PyCoreConfig *core_config = _PyInterpreterState_GetCoreConfig(interp);
319319
dict = _PyCoreConfig_AsDict(core_config);
320320
if (dict == NULL) {
321321
goto error;
@@ -326,7 +326,7 @@ dump_config_impl(void)
326326
Py_CLEAR(dict);
327327

328328
/* main config */
329-
const _PyMainInterpreterConfig *main_config = &interp->config;
329+
const _PyMainInterpreterConfig *main_config = _PyInterpreterState_GetMainConfig(interp);
330330
dict = _PyMainInterpreterConfig_AsDict(main_config);
331331
if (dict == NULL) {
332332
goto error;

Python/pystate.c

+11
Original file line numberDiff line numberDiff line change
@@ -407,6 +407,17 @@ _PyInterpreterState_IDDecref(PyInterpreterState *interp)
407407
}
408408
}
409409

410+
_PyCoreConfig *
411+
_PyInterpreterState_GetCoreConfig(PyInterpreterState *interp)
412+
{
413+
return &interp->core_config;
414+
}
415+
416+
_PyMainInterpreterConfig *
417+
_PyInterpreterState_GetMainConfig(PyInterpreterState *interp)
418+
{
419+
return &interp->config;
420+
}
410421

411422
/* Default implementation for _PyThreadState_GetFrame */
412423
static struct _frame *

0 commit comments

Comments
 (0)