Skip to content

Commit 81c7204

Browse files
bpo-46541: Replace core use of _Py_IDENTIFIER() with statically initialized global objects. (gh-30928)
We're no longer using _Py_IDENTIFIER() (or _Py_static_string()) in any core CPython code. It is still used in a number of non-builtin stdlib modules. The replacement is: PyUnicodeObject (not pointer) fields under _PyRuntimeState, statically initialized as part of _PyRuntime. A new _Py_GET_GLOBAL_IDENTIFIER() macro facilitates lookup of the fields (along with _Py_GET_GLOBAL_STRING() for non-identifier strings). https://bugs.python.org/issue46541#msg411799 explains the rationale for this change. The core of the change is in: * (new) Include/internal/pycore_global_strings.h - the declarations for the global strings, along with the macros * Include/internal/pycore_runtime_init.h - added the static initializers for the global strings * Include/internal/pycore_global_objects.h - where the struct in pycore_global_strings.h is hooked into _PyRuntimeState * Tools/scripts/generate_global_objects.py - added generation of the global string declarations and static initializers I've also added a --check flag to generate_global_objects.py (along with make check-global-objects) to check for unused global strings. That check is added to the PR CI config. The remainder of this change updates the core code to use _Py_GET_GLOBAL_IDENTIFIER() instead of _Py_IDENTIFIER() and the related _Py*Id functions (likewise for _Py_GET_GLOBAL_STRING() instead of _Py_static_string()). This includes adding a few functions where there wasn't already an alternative to _Py*Id(), replacing the _Py_Identifier * parameter with PyObject *. The following are not changed (yet): * stop using _Py_IDENTIFIER() in the stdlib modules * (maybe) get rid of _Py_IDENTIFIER(), etc. entirely -- this may not be doable as at least one package on PyPI using this (private) API * (maybe) intern the strings during runtime init https://bugs.python.org/issue46541
1 parent c018d30 commit 81c7204

File tree

108 files changed

+2282
-1573
lines changed

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

108 files changed

+2282
-1573
lines changed

.github/workflows/build.yml

+2
Original file line numberDiff line numberDiff line change
@@ -100,6 +100,8 @@ jobs:
100100
run: make smelly
101101
- name: Check limited ABI symbols
102102
run: make check-limited-abi
103+
- name: Check global objects
104+
run: make check-global-objects
103105

104106
build_win32:
105107
name: 'Windows (x86)'

Include/cpython/abstract.h

+4
Original file line numberDiff line numberDiff line change
@@ -116,6 +116,10 @@ PyObject_CallMethodOneArg(PyObject *self, PyObject *name, PyObject *arg)
116116
2 | PY_VECTORCALL_ARGUMENTS_OFFSET, NULL);
117117
}
118118

119+
PyAPI_FUNC(PyObject *) _PyObject_CallMethod(PyObject *obj,
120+
PyObject *name,
121+
const char *format, ...);
122+
119123
/* Like PyObject_CallMethod(), but expect a _Py_Identifier*
120124
as the method name. */
121125
PyAPI_FUNC(PyObject *) _PyObject_CallMethodId(PyObject *obj,

Include/cpython/ceval.h

+1
Original file line numberDiff line numberDiff line change
@@ -15,6 +15,7 @@ PyAPI_FUNC(int) _PyEval_SetAsyncGenFinalizer(PyObject *);
1515
PyAPI_FUNC(PyObject *) _PyEval_GetAsyncGenFinalizer(void);
1616

1717
/* Helper to look up a builtin object */
18+
PyAPI_FUNC(PyObject *) _PyEval_GetBuiltin(PyObject *);
1819
PyAPI_FUNC(PyObject *) _PyEval_GetBuiltinId(_Py_Identifier *);
1920
/* Look at the current frame's (if any) code's co_flags, and turn on
2021
the corresponding compiler flags in cf->cf_flags. Return 1 if any

Include/cpython/dictobject.h

+1
Original file line numberDiff line numberDiff line change
@@ -30,6 +30,7 @@ typedef struct {
3030

3131
PyAPI_FUNC(PyObject *) _PyDict_GetItem_KnownHash(PyObject *mp, PyObject *key,
3232
Py_hash_t hash);
33+
PyAPI_FUNC(PyObject *) _PyDict_GetItemWithError(PyObject *dp, PyObject *key);
3334
PyAPI_FUNC(PyObject *) _PyDict_GetItemIdWithError(PyObject *dp,
3435
struct _Py_Identifier *key);
3536
PyAPI_FUNC(PyObject *) _PyDict_GetItemStringWithError(PyObject *, const char *);

Include/cpython/object.h

+12-1
Original file line numberDiff line numberDiff line change
@@ -43,10 +43,16 @@ typedef struct _Py_Identifier {
4343
Py_ssize_t index;
4444
} _Py_Identifier;
4545

46+
#if defined(NEEDS_PY_IDENTIFIER) || !defined(Py_BUILD_CORE)
47+
// For now we are keeping _Py_IDENTIFIER for continued use
48+
// in non-builtin extensions (and naughty PyPI modules).
49+
4650
#define _Py_static_string_init(value) { .string = value, .index = -1 }
4751
#define _Py_static_string(varname, value) static _Py_Identifier varname = _Py_static_string_init(value)
4852
#define _Py_IDENTIFIER(varname) _Py_static_string(PyId_##varname, #varname)
4953

54+
#endif /* NEEDS_PY_IDENTIFIER */
55+
5056
typedef int (*getbufferproc)(PyObject *, Py_buffer *, int);
5157
typedef void (*releasebufferproc)(PyObject *, Py_buffer *);
5258

@@ -249,7 +255,12 @@ typedef struct _heaptypeobject {
249255
PyAPI_FUNC(const char *) _PyType_Name(PyTypeObject *);
250256
PyAPI_FUNC(PyObject *) _PyType_Lookup(PyTypeObject *, PyObject *);
251257
PyAPI_FUNC(PyObject *) _PyType_LookupId(PyTypeObject *, _Py_Identifier *);
252-
PyAPI_FUNC(PyObject *) _PyObject_LookupSpecial(PyObject *, _Py_Identifier *);
258+
PyAPI_FUNC(PyObject *) _PyObject_LookupSpecialId(PyObject *, _Py_Identifier *);
259+
#ifndef Py_BUILD_CORE
260+
// Backward compatibility for 3rd-party extensions
261+
// that may be using the old name.
262+
#define _PyObject_LookupSpecial _PyObject_LookupSpecialId
263+
#endif
253264
PyAPI_FUNC(PyTypeObject *) _PyType_CalculateMetaclass(PyTypeObject *, PyObject *);
254265
PyAPI_FUNC(PyObject *) _PyType_GetDocFromInternalDoc(const char *, const char *);
255266
PyAPI_FUNC(PyObject *) _PyType_GetTextSignatureFromInternalDoc(const char *, const char *);

Include/cpython/sysmodule.h

+2
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,8 @@
22
# error "this header file must not be included directly"
33
#endif
44

5+
PyAPI_FUNC(PyObject *) _PySys_GetAttr(PyThreadState *tstate,
6+
PyObject *name);
57
PyAPI_FUNC(PyObject *) _PySys_GetObjectId(_Py_Identifier *key);
68
PyAPI_FUNC(int) _PySys_SetObjectId(_Py_Identifier *key, PyObject *);
79

Include/internal/pycore_call.h

+3
Original file line numberDiff line numberDiff line change
@@ -30,6 +30,9 @@ PyAPI_FUNC(PyObject *) _PyObject_Call(
3030
PyObject *args,
3131
PyObject *kwargs);
3232

33+
extern PyObject * _PyObject_CallMethodFormat(
34+
PyThreadState *tstate, PyObject *callable, const char *format, ...);
35+
3336

3437
// Static inline variant of public PyVectorcall_Function().
3538
static inline vectorcallfunc

Include/internal/pycore_global_objects.h

+4
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,8 @@ extern "C" {
88
# error "this header requires Py_BUILD_CORE define"
99
#endif
1010

11+
#include "pycore_global_strings.h" // struct _Py_global_strings
12+
1113

1214
// These would be in pycore_long.h if it weren't for an include cycle.
1315
#define _PY_NSMALLPOSINTS 257
@@ -36,6 +38,8 @@ struct _Py_global_objects {
3638
PyBytesObject ob;
3739
char eos;
3840
} bytes_characters[256];
41+
42+
struct _Py_global_strings strings;
3943
} singletons;
4044
};
4145

0 commit comments

Comments
 (0)