GeezerVison is showing github.com — Add test for _Py_OPAQUE_PYOBJECT · python/cpython@33d9ae7
File treeExpand file treeCollapse file tree Open diff view settings Expand file treeCollapse file tree Open diff view settings Collapse file Copy file name to clipboardExpand all lines: Lib/test/test_cext/__init__.py +16-5Lines changed: 16 additions & 5 deletions | Original file line number | Diff line number | Diff line change |
|---|
| 12 | 12 | from test import support | 13 | 13 |
| 14 | 14 |
| 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 | +] | 16 | 19 | SETUP = os.path.join(os.path.dirname(__file__), 'setup.py') | 17 | 20 |
| 18 | 21 |
| @@ -51,24 +54,32 @@ def test_build_limited(self): | 51 | 54 | def test_build_limited_c11(self): | 52 | 55 | self.check_build('_test_limited_c11_cext', limited=True, std='c11') | 53 | 56 |
| 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): | 55 | 62 | venv_dir = 'env' | 56 | 63 | with support.setup_venv_with_pip_setuptools(venv_dir) as python_exe: | 57 | 64 | self._check_build(extension_name, python_exe, | 58 | | - std=std, limited=limited) | | 65 | + std=std, limited=limited, opaque=opaque) | 59 | 66 |
| 60 | | - def _check_build(self, extension_name, python_exe, std, limited): | | 67 | + def _check_build(self, extension_name, python_exe, std, limited, opaque): | 61 | 68 | pkg_dir = 'pkg' | 62 | 69 | os.mkdir(pkg_dir) | 63 | 70 | 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) | 65 | 74 |
| 66 | 75 | def run_cmd(operation, cmd): | 67 | 76 | env = os.environ.copy() | 68 | 77 | if std: | 69 | 78 | env['CPYTHON_TEST_STD'] = std | 70 | 79 | if limited: | 71 | 80 | env['CPYTHON_TEST_LIMITED'] = '1' | | 81 | + if opaque: | | 82 | + env['CPYTHON_TEST_OPAQUE_PYOBJECT'] = '1' | 72 | 83 | env['CPYTHON_TEST_EXT_NAME'] = extension_name | 73 | 84 | if support.verbose: | 74 | 85 | print('Run:', ' '.join(map(shlex.quote, cmd))) | |
Collapse file Copy file name to clipboard +29Lines changed: 29 additions & 0 deletions | Original file line number | Diff line number | Diff line change |
|---|
| | 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 | +} |
Collapse file Copy file name to clipboardExpand all lines: Lib/test/test_cext/extension.c +27-4Lines changed: 27 additions & 4 deletions | Original file line number | Diff line number | Diff line change |
|---|
@@ -58,6 +58,9 @@ _testcext_exec( | 58 | 58 | return 0; | 59 | 59 | } | 60 | 60 |
| | 61 | +#define _FUNC_NAME(NAME) PyInit_ ## NAME | | 62 | +#define FUNC_NAME(NAME) _FUNC_NAME(NAME) | | 63 | + | 61 | 64 | // Converting from function pointer to void* has undefined behavior, but | 62 | 65 | // works on all known platforms, and CPython's module and type slots currently | 63 | 66 | // need it. | @@ -76,9 +79,10 @@ static PyModuleDef_Slot _testcext_slots[] = { | 76 | 79 |
| 77 | 80 | _Py_COMP_DIAG_POP | 78 | 81 |
| 79 | | - | 80 | 82 | PyDoc_STRVAR(_testcext_doc, "C test extension."); | 81 | 83 |
| | 84 | +#ifndef _Py_OPAQUE_PYOBJECT | | 85 | + | 82 | 86 | static struct PyModuleDef _testcext_module = { | 83 | 87 | PyModuleDef_HEAD_INIT, // m_base | 84 | 88 | STR(MODULE_NAME), // m_name | @@ -92,11 +96,30 @@ static struct PyModuleDef _testcext_module = { | 92 | 96 | }; | 93 | 97 |
| 94 | 98 |
| 95 | | -#define _FUNC_NAME(NAME) PyInit_ ## NAME | 96 | | -#define FUNC_NAME(NAME) _FUNC_NAME(NAME) | 97 | | - | 98 | 99 | PyMODINIT_FUNC | 99 | 100 | FUNC_NAME(MODULE_NAME)(void) | 100 | 101 | { | 101 | 102 | return PyModuleDef_Init(&_testcext_module); | 102 | 103 | } | | 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 |
Collapse file Copy file name to clipboardExpand all lines: Lib/test/test_cext/setup.py +10-1Lines changed: 10 additions & 1 deletion | Original file line number | Diff line number | Diff line change |
|---|
@@ -45,6 +45,9 @@ def main(): | 45 | 45 | std = os.environ.get("CPYTHON_TEST_STD", "") | 46 | 46 | module_name = os.environ["CPYTHON_TEST_EXT_NAME"] | 47 | 47 | limited = bool(os.environ.get("CPYTHON_TEST_LIMITED", "")) | | 48 | + opaque = bool(os.environ.get("CPYTHON_TEST_OPAQUE_PYOBJECT", "")) | | 49 | + | | 50 | + sources = [SOURCE] | 48 | 51 |
| 49 | 52 | cflags = list(CFLAGS) | 50 | 53 | cflags.append(f'-DMODULE_NAME={module_name}') | @@ -75,6 +78,12 @@ def main(): | 75 | 78 | version = sys.hexversion | 76 | 79 | cflags.append(f'-DPy_LIMITED_API={version:#x}') | 77 | 80 |
| | 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 | + | 78 | 87 | # On Windows, add PCbuild\amd64\ to include and library directories | 79 | 88 | include_dirs = [] | 80 | 89 | library_dirs = [] | @@ -99,7 +108,7 @@ def main(): | 99 | 108 |
| 100 | 109 | ext = Extension( | 101 | 110 | module_name, | 102 | | - sources=[SOURCE], | | 111 | + sources=sources, | 103 | 112 | extra_compile_args=cflags, | 104 | 113 | include_dirs=include_dirs, | 105 | 114 | library_dirs=library_dirs) | |
|