Movatterモバイル変換


[0]ホーム

URL:


Skip to content

Navigation Menu

Sign in
Appearance settings

Search code, repositories, users, issues, pull requests...

Provide feedback

We read every piece of feedback, and take your input very seriously.

Saved searches

Use saved searches to filter your results more quickly

Sign up
Appearance settings

Commitc7d24b8

Browse files
encukouvstinner
andauthored
gh-111506: Add _Py_OPAQUE_PYOBJECT to hide PyObject layout & related API (GH-136505)
Allow Py_LIMITED_API for (Py_GIL_DISABLED && _Py_OPAQUE_PYOBJECT)API that's removed when _Py_OPAQUE_PYOBJECT is defined: - PyObject_HEAD - _PyObject_EXTRA_INIT - PyObject_HEAD_INIT - PyObject_VAR_HEAD - struct _object (i.e. PyObject) (opaque) - struct PyVarObject (opaque) - Py_SIZE - Py_SET_TYPE - Py_SET_SIZE - PyModuleDef_Base (opaque) - PyModuleDef_HEAD_INIT - PyModuleDef (opaque) - _Py_IsImmortal - _Py_IsStaticImmortalNote that the `_Py_IsImmortal` removal (and a few other issues) means _Py_OPAQUE_PYOBJECT only works with limitedAPI 3.14+ now.Co-authored-by: Victor Stinner <vstinner@python.org>
1 parentdb47f4d commitc7d24b8

File tree

8 files changed

+123
-25
lines changed

8 files changed

+123
-25
lines changed

‎Include/Python.h

Lines changed: 11 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -45,19 +45,19 @@
4545
# endif
4646
#endif
4747

48-
// gh-111506: The free-threaded build is not compatible with the limited API
49-
// or the stable ABI.
50-
#if defined(Py_LIMITED_API)&& defined(Py_GIL_DISABLED)
51-
# error "The limited API is not currently supported in the free-threaded build"
52-
#endif
48+
#if defined(Py_GIL_DISABLED)
49+
# if defined(Py_LIMITED_API)&& !defined(_Py_OPAQUE_PYOBJECT)
50+
# error "Py_LIMITED_API is not currently supported in the free-threaded build"
51+
# endif
5352

54-
#if defined(Py_GIL_DISABLED)&& defined(_MSC_VER)
55-
# include<intrin.h>// __readgsqword()
56-
#endif
53+
# if defined(_MSC_VER)
54+
#include<intrin.h>// __readgsqword()
55+
#endif
5756

58-
#if defined(Py_GIL_DISABLED)&& defined(__MINGW32__)
59-
# include<intrin.h>// __readgsqword()
60-
#endif
57+
# if defined(__MINGW32__)
58+
# include<intrin.h>// __readgsqword()
59+
# endif
60+
#endif// Py_GIL_DISABLED
6161

6262
// Include Python header files
6363
#include"pyport.h"

‎Include/moduleobject.h

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -36,6 +36,7 @@ PyAPI_FUNC(PyObject *) PyModuleDef_Init(PyModuleDef*);
3636
PyAPI_DATA(PyTypeObject)PyModuleDef_Type;
3737
#endif
3838

39+
#ifndef_Py_OPAQUE_PYOBJECT
3940
typedefstructPyModuleDef_Base {
4041
PyObject_HEAD
4142
/* The function used to re-initialize the module.
@@ -63,6 +64,7 @@ typedef struct PyModuleDef_Base {
6364
0,/* m_index */ \
6465
_Py_NULL,/* m_copy */ \
6566
}
67+
#endif// _Py_OPAQUE_PYOBJECT
6668

6769
#if !defined(Py_LIMITED_API)||Py_LIMITED_API+0 >=0x03050000
6870
/* New in 3.5 */
@@ -104,6 +106,8 @@ struct PyModuleDef_Slot {
104106
PyAPI_FUNC(int)PyUnstable_Module_SetGIL(PyObject*module,void*gil);
105107
#endif
106108

109+
110+
#ifndef_Py_OPAQUE_PYOBJECT
107111
structPyModuleDef {
108112
PyModuleDef_Basem_base;
109113
constchar*m_name;
@@ -115,6 +119,7 @@ struct PyModuleDef {
115119
inquirym_clear;
116120
freefuncm_free;
117121
};
122+
#endif// _Py_OPAQUE_PYOBJECT
118123

119124
#ifdef__cplusplus
120125
}

‎Include/object.h

Lines changed: 20 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -56,6 +56,11 @@ whose size is determined when the object is allocated.
5656
# definePy_REF_DEBUG
5757
#endif
5858

59+
#if defined(_Py_OPAQUE_PYOBJECT)&& !defined(Py_LIMITED_API)
60+
# error "_Py_OPAQUE_PYOBJECT only makes sense with Py_LIMITED_API"
61+
#endif
62+
63+
#ifndef_Py_OPAQUE_PYOBJECT
5964
/* PyObject_HEAD defines the initial segment of every PyObject. */
6065
#definePyObject_HEAD PyObject ob_base;
6166

@@ -99,6 +104,8 @@ whose size is determined when the object is allocated.
99104
* not necessarily a byte count.
100105
*/
101106
#definePyObject_VAR_HEAD PyVarObject ob_base;
107+
#endif// !defined(_Py_OPAQUE_PYOBJECT)
108+
102109
#definePy_INVALID_SIZE (Py_ssize_t)-1
103110

104111
/* PyObjects are given a minimum alignment so that the least significant bits
@@ -112,7 +119,9 @@ whose size is determined when the object is allocated.
112119
* by hand. Similarly every pointer to a variable-size Python object can,
113120
* in addition, be cast to PyVarObject*.
114121
*/
115-
#ifndefPy_GIL_DISABLED
122+
#ifdef_Py_OPAQUE_PYOBJECT
123+
/* PyObject is opaque */
124+
#elif !defined(Py_GIL_DISABLED)
116125
struct_object {
117126
#if (defined(__GNUC__)|| defined(__clang__)) \
118127
&& !(defined__STDC_VERSION__&&__STDC_VERSION__ >=201112L)
@@ -168,15 +177,18 @@ struct _object {
168177
Py_ssize_tob_ref_shared;// shared (atomic) reference count
169178
PyTypeObject*ob_type;
170179
};
171-
#endif
180+
#endif// !defined(_Py_OPAQUE_PYOBJECT)
172181

173182
/* Cast argument to PyObject* type. */
174183
#define_PyObject_CAST(op) _Py_CAST(PyObject*, (op))
175184

176-
typedefstruct {
185+
#ifndef_Py_OPAQUE_PYOBJECT
186+
structPyVarObject {
177187
PyObjectob_base;
178188
Py_ssize_tob_size;/* Number of items in variable part */
179-
}PyVarObject;
189+
};
190+
#endif
191+
typedefstructPyVarObjectPyVarObject;
180192

181193
/* Cast argument to PyVarObject* type. */
182194
#define_PyVarObject_CAST(op) _Py_CAST(PyVarObject*, (op))
@@ -286,6 +298,7 @@ PyAPI_FUNC(PyTypeObject*) Py_TYPE(PyObject *ob);
286298
PyAPI_DATA(PyTypeObject)PyLong_Type;
287299
PyAPI_DATA(PyTypeObject)PyBool_Type;
288300

301+
#ifndef_Py_OPAQUE_PYOBJECT
289302
// bpo-39573: The Py_SET_SIZE() function must be used to set an object size.
290303
staticinlinePy_ssize_tPy_SIZE(PyObject*ob) {
291304
assert(Py_TYPE(ob)!=&PyLong_Type);
@@ -295,6 +308,7 @@ static inline Py_ssize_t Py_SIZE(PyObject *ob) {
295308
#if !defined(Py_LIMITED_API)||Py_LIMITED_API+0<0x030b0000
296309
# definePy_SIZE(ob) Py_SIZE(_PyObject_CAST(ob))
297310
#endif
311+
#endif// !defined(_Py_OPAQUE_PYOBJECT)
298312

299313
staticinlineintPy_IS_TYPE(PyObject*ob,PyTypeObject*type) {
300314
returnPy_TYPE(ob)==type;
@@ -304,6 +318,7 @@ static inline int Py_IS_TYPE(PyObject *ob, PyTypeObject *type) {
304318
#endif
305319

306320

321+
#ifndef_Py_OPAQUE_PYOBJECT
307322
staticinlinevoidPy_SET_TYPE(PyObject*ob,PyTypeObject*type) {
308323
ob->ob_type=type;
309324
}
@@ -323,6 +338,7 @@ static inline void Py_SET_SIZE(PyVarObject *ob, Py_ssize_t size) {
323338
#if !defined(Py_LIMITED_API)||Py_LIMITED_API+0<0x030b0000
324339
# definePy_SET_SIZE(ob,size) Py_SET_SIZE(_PyVarObject_CAST(ob), (size))
325340
#endif
341+
#endif// !defined(_Py_OPAQUE_PYOBJECT)
326342

327343

328344
/*

‎Include/refcount.h

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -117,6 +117,7 @@ PyAPI_FUNC(Py_ssize_t) Py_REFCNT(PyObject *ob);
117117
#endif
118118
#endif
119119

120+
#ifndef_Py_OPAQUE_PYOBJECT
120121
staticinlinePy_ALWAYS_INLINEint_Py_IsImmortal(PyObject*op)
121122
{
122123
#if defined(Py_GIL_DISABLED)
@@ -140,6 +141,7 @@ static inline Py_ALWAYS_INLINE int _Py_IsStaticImmortal(PyObject *op)
140141
#endif
141142
}
142143
#define_Py_IsStaticImmortal(op) _Py_IsStaticImmortal(_PyObject_CAST(op))
144+
#endif// !defined(_Py_OPAQUE_PYOBJECT)
143145

144146
// Py_SET_REFCNT() implementation for stable ABI
145147
PyAPI_FUNC(void)_Py_SetRefcnt(PyObject*ob,Py_ssize_trefcnt);

‎Lib/test/test_cext/__init__.py

Lines changed: 20 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -12,7 +12,10 @@
1212
fromtestimportsupport
1313

1414

15-
SOURCE=os.path.join(os.path.dirname(__file__),'extension.c')
15+
SOURCES= [
16+
os.path.join(os.path.dirname(__file__),'extension.c'),
17+
os.path.join(os.path.dirname(__file__),'create_moduledef.c'),
18+
]
1619
SETUP=os.path.join(os.path.dirname(__file__),'setup.py')
1720

1821

@@ -35,24 +38,31 @@ class BaseTests:
3538
deftest_build(self):
3639
self.check_build('_test_cext')
3740

38-
defcheck_build(self,extension_name,std=None,limited=False):
41+
defcheck_build(self,extension_name,std=None,limited=False,
42+
opaque_pyobject=False):
3943
venv_dir='env'
4044
withsupport.setup_venv_with_pip_setuptools(venv_dir)aspython_exe:
4145
self._check_build(extension_name,python_exe,
42-
std=std,limited=limited)
46+
std=std,limited=limited,
47+
opaque_pyobject=opaque_pyobject)
4348

44-
def_check_build(self,extension_name,python_exe,std,limited):
49+
def_check_build(self,extension_name,python_exe,std,limited,
50+
opaque_pyobject):
4551
pkg_dir='pkg'
4652
os.mkdir(pkg_dir)
4753
shutil.copy(SETUP,os.path.join(pkg_dir,os.path.basename(SETUP)))
48-
shutil.copy(SOURCE,os.path.join(pkg_dir,os.path.basename(SOURCE)))
54+
forsourceinSOURCES:
55+
dest=os.path.join(pkg_dir,os.path.basename(source))
56+
shutil.copy(source,dest)
4957

5058
defrun_cmd(operation,cmd):
5159
env=os.environ.copy()
5260
ifstd:
5361
env['CPYTHON_TEST_STD']=std
5462
iflimited:
5563
env['CPYTHON_TEST_LIMITED']='1'
64+
ifopaque_pyobject:
65+
env['CPYTHON_TEST_OPAQUE_PYOBJECT']='1'
5666
env['CPYTHON_TEST_EXT_NAME']=extension_name
5767
env['TEST_INTERNAL_C_API']=str(int(self.TEST_INTERNAL_C_API))
5868
ifsupport.verbose:
@@ -107,6 +117,11 @@ def test_build_limited_c11(self):
107117
deftest_build_c11(self):
108118
self.check_build('_test_c11_cext',std='c11')
109119

120+
deftest_build_opaque_pyobject(self):
121+
# Test with _Py_OPAQUE_PYOBJECT
122+
self.check_build('_test_limited_opaque_cext',limited=True,
123+
opaque_pyobject=True)
124+
110125
@unittest.skipIf(support.MS_WINDOWS,"MSVC doesn't support /std:c99")
111126
deftest_build_c99(self):
112127
# In public docs, we say C API is compatible with C11. However,

‎Lib/test/test_cext/create_moduledef.c

Lines changed: 29 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,29 @@
1+
// Workaround for testing _Py_OPAQUE_PYOBJECT.
2+
// See end of 'extension.c'
3+
4+
5+
#undef _Py_OPAQUE_PYOBJECT
6+
#undef Py_LIMITED_API
7+
#include"Python.h"
8+
9+
10+
// (repeated definition to avoid creating a header)
11+
externPyObject*testcext_create_moduledef(
12+
constchar*name,constchar*doc,
13+
PyMethodDef*methods,PyModuleDef_Slot*slots);
14+
15+
PyObject*testcext_create_moduledef(
16+
constchar*name,constchar*doc,
17+
PyMethodDef*methods,PyModuleDef_Slot*slots) {
18+
19+
staticstructPyModuleDef_testcext_module= {
20+
PyModuleDef_HEAD_INIT,
21+
};
22+
if (!_testcext_module.m_name) {
23+
_testcext_module.m_name=name;
24+
_testcext_module.m_doc=doc;
25+
_testcext_module.m_methods=methods;
26+
_testcext_module.m_slots=slots;
27+
}
28+
returnPyModuleDef_Init(&_testcext_module);
29+
}

‎Lib/test/test_cext/extension.c

Lines changed: 27 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -78,6 +78,9 @@ _testcext_exec(
7878
return0;
7979
}
8080

81+
#define_FUNC_NAME(NAME) PyInit_ ## NAME
82+
#defineFUNC_NAME(NAME) _FUNC_NAME(NAME)
83+
8184
// Converting from function pointer to void* has undefined behavior, but
8285
// works on all known platforms, and CPython's module and type slots currently
8386
// need it.
@@ -96,9 +99,10 @@ static PyModuleDef_Slot _testcext_slots[] = {
9699

97100
_Py_COMP_DIAG_POP
98101

99-
100102
PyDoc_STRVAR(_testcext_doc,"C test extension.");
101103

104+
#ifndef_Py_OPAQUE_PYOBJECT
105+
102106
staticstructPyModuleDef_testcext_module= {
103107
PyModuleDef_HEAD_INIT,// m_base
104108
STR(MODULE_NAME),// m_name
@@ -112,11 +116,30 @@ static struct PyModuleDef _testcext_module = {
112116
};
113117

114118

115-
#define_FUNC_NAME(NAME) PyInit_ ## NAME
116-
#defineFUNC_NAME(NAME) _FUNC_NAME(NAME)
117-
118119
PyMODINIT_FUNC
119120
FUNC_NAME(MODULE_NAME)(void)
120121
{
121122
returnPyModuleDef_Init(&_testcext_module);
122123
}
124+
125+
#else// _Py_OPAQUE_PYOBJECT
126+
127+
// Opaque PyObject means that PyModuleDef is also opaque and cannot be
128+
// declared statically. See PEP 793.
129+
// So, this part of module creation is split into a separate source file
130+
// which uses non-limited API.
131+
132+
// (repeated definition to avoid creating a header)
133+
externPyObject*testcext_create_moduledef(
134+
constchar*name,constchar*doc,
135+
PyMethodDef*methods,PyModuleDef_Slot*slots);
136+
137+
138+
PyMODINIT_FUNC
139+
FUNC_NAME(MODULE_NAME)(void)
140+
{
141+
returntestcext_create_moduledef(
142+
STR(MODULE_NAME),_testcext_doc,_testcext_methods,_testcext_slots);
143+
}
144+
145+
#endif// _Py_OPAQUE_PYOBJECT

‎Lib/test/test_cext/setup.py

Lines changed: 9 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -59,8 +59,11 @@ def main():
5959
std=os.environ.get("CPYTHON_TEST_STD","")
6060
module_name=os.environ["CPYTHON_TEST_EXT_NAME"]
6161
limited=bool(os.environ.get("CPYTHON_TEST_LIMITED",""))
62+
opaque_pyobject=bool(os.environ.get("CPYTHON_TEST_OPAQUE_PYOBJECT",""))
6263
internal=bool(int(os.environ.get("TEST_INTERNAL_C_API","0")))
6364

65+
sources= [SOURCE]
66+
6467
ifnotinternal:
6568
cflags=list(PUBLIC_CFLAGS)
6669
else:
@@ -93,6 +96,11 @@ def main():
9396
version=sys.hexversion
9497
cflags.append(f'-DPy_LIMITED_API={version:#x}')
9598

99+
# Define _Py_OPAQUE_PYOBJECT macro
100+
ifopaque_pyobject:
101+
cflags.append(f'-D_Py_OPAQUE_PYOBJECT')
102+
sources.append('create_moduledef.c')
103+
96104
ifinternal:
97105
cflags.append('-DTEST_INTERNAL_C_API=1')
98106

@@ -120,7 +128,7 @@ def main():
120128

121129
ext=Extension(
122130
module_name,
123-
sources=[SOURCE],
131+
sources=sources,
124132
extra_compile_args=cflags,
125133
include_dirs=include_dirs,
126134
library_dirs=library_dirs)

0 commit comments

Comments
 (0)

[8]ページ先頭

©2009-2025 Movatter.jp