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)
pFad - Phonifier reborn

Pfad - The Proxy pFad of © 2024 Garber Painting. All rights reserved.

Note: This service is not intended for secure transactions such as banking, social media, email, or purchasing. Use at your own risk. We assume no liability whatsoever for broken pages.


Alternative Proxies:

Alternative Proxy

pFad Proxy

pFad v3 Proxy

pFad v4 Proxy