Skip to content

Commit 33d9ae7

Browse files
committed
Add test for _Py_OPAQUE_PYOBJECT
1 parent 7ff56f4 commit 33d9ae7

File tree

4 files changed

+82
-10
lines changed

4 files changed

+82
-10
lines changed

Lib/test/test_cext/__init__.py

Lines changed: 16 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -12,7 +12,10 @@
1212
from test import support
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

@@ -51,24 +54,32 @@ def test_build_limited(self):
5154
def test_build_limited_c11(self):
5255
self.check_build('_test_limited_c11_cext', limited=True, std='c11')
5356

54-
def check_build(self, extension_name, std=None, limited=False):
57+
def test_build_opaque(self):
58+
# Test with _Py_OPAQUE_PYOBJECT
59+
self.check_build('_test_limited_opaque_cext', limited=True, opaque=True)
60+
61+
def check_build(self, extension_name, std=None, limited=False, opaque=False):
5562
venv_dir = 'env'
5663
with support.setup_venv_with_pip_setuptools(venv_dir) as python_exe:
5764
self._check_build(extension_name, python_exe,
58-
std=std, limited=limited)
65+
std=std, limited=limited, opaque=opaque)
5966

60-
def _check_build(self, extension_name, python_exe, std, limited):
67+
def _check_build(self, extension_name, python_exe, std, limited, opaque):
6168
pkg_dir = 'pkg'
6269
os.mkdir(pkg_dir)
6370
shutil.copy(SETUP, os.path.join(pkg_dir, os.path.basename(SETUP)))
64-
shutil.copy(SOURCE, os.path.join(pkg_dir, os.path.basename(SOURCE)))
71+
for source in SOURCES:
72+
dest = os.path.join(pkg_dir, os.path.basename(source))
73+
shutil.copy(source, dest)
6574

6675
def run_cmd(operation, cmd):
6776
env = os.environ.copy()
6877
if std:
6978
env['CPYTHON_TEST_STD'] = std
7079
if limited:
7180
env['CPYTHON_TEST_LIMITED'] = '1'
81+
if opaque:
82+
env['CPYTHON_TEST_OPAQUE_PYOBJECT'] = '1'
7283
env['CPYTHON_TEST_EXT_NAME'] = extension_name
7384
if support.verbose:
7485
print('Run:', ' '.join(map(shlex.quote, cmd)))

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+
extern PyObject *testcext_create_moduledef(
12+
const char *name, const char *doc,
13+
PyMethodDef *methods, PyModuleDef_Slot *slots);
14+
15+
PyObject *testcext_create_moduledef(
16+
const char *name, const char *doc,
17+
PyMethodDef *methods, PyModuleDef_Slot *slots) {
18+
19+
static struct PyModuleDef _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+
return PyModuleDef_Init(&_testcext_module);
29+
}

Lib/test/test_cext/extension.c

Lines changed: 27 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -58,6 +58,9 @@ _testcext_exec(
5858
return 0;
5959
}
6060

61+
#define _FUNC_NAME(NAME) PyInit_ ## NAME
62+
#define FUNC_NAME(NAME) _FUNC_NAME(NAME)
63+
6164
// Converting from function pointer to void* has undefined behavior, but
6265
// works on all known platforms, and CPython's module and type slots currently
6366
// need it.
@@ -76,9 +79,10 @@ static PyModuleDef_Slot _testcext_slots[] = {
7679

7780
_Py_COMP_DIAG_POP
7881

79-
8082
PyDoc_STRVAR(_testcext_doc, "C test extension.");
8183

84+
#ifndef _Py_OPAQUE_PYOBJECT
85+
8286
static struct PyModuleDef _testcext_module = {
8387
PyModuleDef_HEAD_INIT, // m_base
8488
STR(MODULE_NAME), // m_name
@@ -92,11 +96,30 @@ static struct PyModuleDef _testcext_module = {
9296
};
9397

9498

95-
#define _FUNC_NAME(NAME) PyInit_ ## NAME
96-
#define FUNC_NAME(NAME) _FUNC_NAME(NAME)
97-
9899
PyMODINIT_FUNC
99100
FUNC_NAME(MODULE_NAME)(void)
100101
{
101102
return PyModuleDef_Init(&_testcext_module);
102103
}
104+
105+
#else // _Py_OPAQUE_PYOBJECT
106+
107+
// Opaque PyObject means that PyModuleDef is also opaque and cannot be
108+
// declared statically. See PEP 793.
109+
// So, this part of module creation is split into a separate source file
110+
// which uses non-limited API.
111+
112+
// (repeated definition to avoid creating a header)
113+
extern PyObject *testcext_create_moduledef(
114+
const char *name, const char *doc,
115+
PyMethodDef *methods, PyModuleDef_Slot *slots);
116+
117+
118+
PyMODINIT_FUNC
119+
FUNC_NAME(MODULE_NAME)(void)
120+
{
121+
return testcext_create_moduledef(
122+
STR(MODULE_NAME), _testcext_doc, _testcext_methods, _testcext_slots);
123+
}
124+
125+
#endif // _Py_OPAQUE_PYOBJECT

Lib/test/test_cext/setup.py

Lines changed: 10 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -45,6 +45,9 @@ def main():
4545
std = os.environ.get("CPYTHON_TEST_STD", "")
4646
module_name = os.environ["CPYTHON_TEST_EXT_NAME"]
4747
limited = bool(os.environ.get("CPYTHON_TEST_LIMITED", ""))
48+
opaque = bool(os.environ.get("CPYTHON_TEST_OPAQUE_PYOBJECT", ""))
49+
50+
sources = [SOURCE]
4851

4952
cflags = list(CFLAGS)
5053
cflags.append(f'-DMODULE_NAME={module_name}')
@@ -75,6 +78,12 @@ def main():
7578
version = sys.hexversion
7679
cflags.append(f'-DPy_LIMITED_API={version:#x}')
7780

81+
# Define _Py_OPAQUE_PYOBJECT macro
82+
if opaque:
83+
version = sys.hexversion
84+
cflags.append(f'-D_Py_OPAQUE_PYOBJECT')
85+
sources.append('create_moduledef.c')
86+
7887
# On Windows, add PCbuild\amd64\ to include and library directories
7988
include_dirs = []
8089
library_dirs = []
@@ -99,7 +108,7 @@ def main():
99108

100109
ext = Extension(
101110
module_name,
102-
sources=[SOURCE],
111+
sources=sources,
103112
extra_compile_args=cflags,
104113
include_dirs=include_dirs,
105114
library_dirs=library_dirs)

0 commit comments

Comments
 (0)