mirror of
				https://github.com/python/cpython.git
				synced 2025-10-31 05:31:20 +00:00 
			
		
		
		
	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_IsStaticImmortal
Note that the `_Py_IsImmortal` removal (and a few other issues)
 means _Py_OPAQUE_PYOBJECT only works with limited
API 3.14+ now.
Co-authored-by: Victor Stinner <vstinner@python.org>
			
			
This commit is contained in:
		
							parent
							
								
									db47f4d844
								
							
						
					
					
						commit
						c7d24b81c3
					
				
					 8 changed files with 123 additions and 25 deletions
				
			
		|  | @ -45,19 +45,19 @@ | ||||||
| #  endif | #  endif | ||||||
| #endif | #endif | ||||||
| 
 | 
 | ||||||
| // gh-111506: The free-threaded build is not compatible with the limited API
 | #if defined(Py_GIL_DISABLED) | ||||||
| // or the stable ABI.
 | #  if defined(Py_LIMITED_API) && !defined(_Py_OPAQUE_PYOBJECT) | ||||||
| #if defined(Py_LIMITED_API) && defined(Py_GIL_DISABLED) | #    error "Py_LIMITED_API is not currently supported in the free-threaded build" | ||||||
| #  error "The limited API is not currently supported in the free-threaded build" | #  endif | ||||||
| #endif |  | ||||||
| 
 | 
 | ||||||
| #if defined(Py_GIL_DISABLED) && defined(_MSC_VER) | #  if defined(_MSC_VER) | ||||||
| #    include <intrin.h>             // __readgsqword()
 | #    include <intrin.h>             // __readgsqword()
 | ||||||
| #endif | #  endif | ||||||
| 
 | 
 | ||||||
| #if defined(Py_GIL_DISABLED) && defined(__MINGW32__) | #  if defined(__MINGW32__) | ||||||
| #    include <intrin.h>             // __readgsqword()
 | #    include <intrin.h>             // __readgsqword()
 | ||||||
| #endif | #  endif | ||||||
|  | #endif // Py_GIL_DISABLED
 | ||||||
| 
 | 
 | ||||||
| // Include Python header files
 | // Include Python header files
 | ||||||
| #include "pyport.h" | #include "pyport.h" | ||||||
|  |  | ||||||
|  | @ -36,6 +36,7 @@ PyAPI_FUNC(PyObject *) PyModuleDef_Init(PyModuleDef*); | ||||||
| PyAPI_DATA(PyTypeObject) PyModuleDef_Type; | PyAPI_DATA(PyTypeObject) PyModuleDef_Type; | ||||||
| #endif | #endif | ||||||
| 
 | 
 | ||||||
|  | #ifndef _Py_OPAQUE_PYOBJECT | ||||||
| typedef struct PyModuleDef_Base { | typedef struct PyModuleDef_Base { | ||||||
|   PyObject_HEAD |   PyObject_HEAD | ||||||
|   /* The function used to re-initialize the module.
 |   /* The function used to re-initialize the module.
 | ||||||
|  | @ -63,6 +64,7 @@ typedef struct PyModuleDef_Base { | ||||||
|     0,        /* m_index */      \ |     0,        /* m_index */      \ | ||||||
|     _Py_NULL, /* m_copy */       \ |     _Py_NULL, /* m_copy */       \ | ||||||
|   } |   } | ||||||
|  | #endif  // _Py_OPAQUE_PYOBJECT
 | ||||||
| 
 | 
 | ||||||
| #if !defined(Py_LIMITED_API) || Py_LIMITED_API+0 >= 0x03050000 | #if !defined(Py_LIMITED_API) || Py_LIMITED_API+0 >= 0x03050000 | ||||||
| /* New in 3.5 */ | /* New in 3.5 */ | ||||||
|  | @ -104,6 +106,8 @@ struct PyModuleDef_Slot { | ||||||
| PyAPI_FUNC(int) PyUnstable_Module_SetGIL(PyObject *module, void *gil); | PyAPI_FUNC(int) PyUnstable_Module_SetGIL(PyObject *module, void *gil); | ||||||
| #endif | #endif | ||||||
| 
 | 
 | ||||||
|  | 
 | ||||||
|  | #ifndef _Py_OPAQUE_PYOBJECT | ||||||
| struct PyModuleDef { | struct PyModuleDef { | ||||||
|   PyModuleDef_Base m_base; |   PyModuleDef_Base m_base; | ||||||
|   const char* m_name; |   const char* m_name; | ||||||
|  | @ -115,6 +119,7 @@ struct PyModuleDef { | ||||||
|   inquiry m_clear; |   inquiry m_clear; | ||||||
|   freefunc m_free; |   freefunc m_free; | ||||||
| }; | }; | ||||||
|  | #endif  // _Py_OPAQUE_PYOBJECT
 | ||||||
| 
 | 
 | ||||||
| #ifdef __cplusplus | #ifdef __cplusplus | ||||||
| } | } | ||||||
|  |  | ||||||
|  | @ -56,6 +56,11 @@ whose size is determined when the object is allocated. | ||||||
| #  define Py_REF_DEBUG | #  define Py_REF_DEBUG | ||||||
| #endif | #endif | ||||||
| 
 | 
 | ||||||
|  | #if defined(_Py_OPAQUE_PYOBJECT) && !defined(Py_LIMITED_API) | ||||||
|  | #   error "_Py_OPAQUE_PYOBJECT only makes sense with Py_LIMITED_API" | ||||||
|  | #endif | ||||||
|  | 
 | ||||||
|  | #ifndef _Py_OPAQUE_PYOBJECT | ||||||
| /* PyObject_HEAD defines the initial segment of every PyObject. */ | /* PyObject_HEAD defines the initial segment of every PyObject. */ | ||||||
| #define PyObject_HEAD                   PyObject ob_base; | #define PyObject_HEAD                   PyObject ob_base; | ||||||
| 
 | 
 | ||||||
|  | @ -99,6 +104,8 @@ whose size is determined when the object is allocated. | ||||||
|  * not necessarily a byte count. |  * not necessarily a byte count. | ||||||
|  */ |  */ | ||||||
| #define PyObject_VAR_HEAD      PyVarObject ob_base; | #define PyObject_VAR_HEAD      PyVarObject ob_base; | ||||||
|  | #endif // !defined(_Py_OPAQUE_PYOBJECT)
 | ||||||
|  | 
 | ||||||
| #define Py_INVALID_SIZE (Py_ssize_t)-1 | #define Py_INVALID_SIZE (Py_ssize_t)-1 | ||||||
| 
 | 
 | ||||||
| /* PyObjects are given a minimum alignment so that the least significant bits
 | /* 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. | ||||||
|  * by hand.  Similarly every pointer to a variable-size Python object can, |  * by hand.  Similarly every pointer to a variable-size Python object can, | ||||||
|  * in addition, be cast to PyVarObject*. |  * in addition, be cast to PyVarObject*. | ||||||
|  */ |  */ | ||||||
| #ifndef Py_GIL_DISABLED | #ifdef _Py_OPAQUE_PYOBJECT | ||||||
|  |   /* PyObject is opaque */ | ||||||
|  | #elif !defined(Py_GIL_DISABLED) | ||||||
| struct _object { | struct _object { | ||||||
| #if (defined(__GNUC__) || defined(__clang__)) \ | #if (defined(__GNUC__) || defined(__clang__)) \ | ||||||
|         && !(defined __STDC_VERSION__ && __STDC_VERSION__ >= 201112L) |         && !(defined __STDC_VERSION__ && __STDC_VERSION__ >= 201112L) | ||||||
|  | @ -168,15 +177,18 @@ struct _object { | ||||||
|     Py_ssize_t ob_ref_shared;   // shared (atomic) reference count
 |     Py_ssize_t ob_ref_shared;   // shared (atomic) reference count
 | ||||||
|     PyTypeObject *ob_type; |     PyTypeObject *ob_type; | ||||||
| }; | }; | ||||||
| #endif | #endif // !defined(_Py_OPAQUE_PYOBJECT)
 | ||||||
| 
 | 
 | ||||||
| /* Cast argument to PyObject* type. */ | /* Cast argument to PyObject* type. */ | ||||||
| #define _PyObject_CAST(op) _Py_CAST(PyObject*, (op)) | #define _PyObject_CAST(op) _Py_CAST(PyObject*, (op)) | ||||||
| 
 | 
 | ||||||
| typedef struct { | #ifndef _Py_OPAQUE_PYOBJECT | ||||||
|  | struct PyVarObject { | ||||||
|     PyObject ob_base; |     PyObject ob_base; | ||||||
|     Py_ssize_t ob_size; /* Number of items in variable part */ |     Py_ssize_t ob_size; /* Number of items in variable part */ | ||||||
| } PyVarObject; | }; | ||||||
|  | #endif | ||||||
|  | typedef struct PyVarObject PyVarObject; | ||||||
| 
 | 
 | ||||||
| /* Cast argument to PyVarObject* type. */ | /* Cast argument to PyVarObject* type. */ | ||||||
| #define _PyVarObject_CAST(op) _Py_CAST(PyVarObject*, (op)) | #define _PyVarObject_CAST(op) _Py_CAST(PyVarObject*, (op)) | ||||||
|  | @ -286,6 +298,7 @@ PyAPI_FUNC(PyTypeObject*) Py_TYPE(PyObject *ob); | ||||||
| PyAPI_DATA(PyTypeObject) PyLong_Type; | PyAPI_DATA(PyTypeObject) PyLong_Type; | ||||||
| PyAPI_DATA(PyTypeObject) PyBool_Type; | PyAPI_DATA(PyTypeObject) PyBool_Type; | ||||||
| 
 | 
 | ||||||
|  | #ifndef _Py_OPAQUE_PYOBJECT | ||||||
| // bpo-39573: The Py_SET_SIZE() function must be used to set an object size.
 | // bpo-39573: The Py_SET_SIZE() function must be used to set an object size.
 | ||||||
| static inline Py_ssize_t Py_SIZE(PyObject *ob) { | static inline Py_ssize_t Py_SIZE(PyObject *ob) { | ||||||
|     assert(Py_TYPE(ob) != &PyLong_Type); |     assert(Py_TYPE(ob) != &PyLong_Type); | ||||||
|  | @ -295,6 +308,7 @@ static inline Py_ssize_t Py_SIZE(PyObject *ob) { | ||||||
| #if !defined(Py_LIMITED_API) || Py_LIMITED_API+0 < 0x030b0000 | #if !defined(Py_LIMITED_API) || Py_LIMITED_API+0 < 0x030b0000 | ||||||
| #  define Py_SIZE(ob) Py_SIZE(_PyObject_CAST(ob)) | #  define Py_SIZE(ob) Py_SIZE(_PyObject_CAST(ob)) | ||||||
| #endif | #endif | ||||||
|  | #endif // !defined(_Py_OPAQUE_PYOBJECT)
 | ||||||
| 
 | 
 | ||||||
| static inline int Py_IS_TYPE(PyObject *ob, PyTypeObject *type) { | static inline int Py_IS_TYPE(PyObject *ob, PyTypeObject *type) { | ||||||
|     return Py_TYPE(ob) == type; |     return Py_TYPE(ob) == type; | ||||||
|  | @ -304,6 +318,7 @@ static inline int Py_IS_TYPE(PyObject *ob, PyTypeObject *type) { | ||||||
| #endif | #endif | ||||||
| 
 | 
 | ||||||
| 
 | 
 | ||||||
|  | #ifndef _Py_OPAQUE_PYOBJECT | ||||||
| static inline void Py_SET_TYPE(PyObject *ob, PyTypeObject *type) { | static inline void Py_SET_TYPE(PyObject *ob, PyTypeObject *type) { | ||||||
|     ob->ob_type = type; |     ob->ob_type = type; | ||||||
| } | } | ||||||
|  | @ -323,6 +338,7 @@ static inline void Py_SET_SIZE(PyVarObject *ob, Py_ssize_t size) { | ||||||
| #if !defined(Py_LIMITED_API) || Py_LIMITED_API+0 < 0x030b0000 | #if !defined(Py_LIMITED_API) || Py_LIMITED_API+0 < 0x030b0000 | ||||||
| #  define Py_SET_SIZE(ob, size) Py_SET_SIZE(_PyVarObject_CAST(ob), (size)) | #  define Py_SET_SIZE(ob, size) Py_SET_SIZE(_PyVarObject_CAST(ob), (size)) | ||||||
| #endif | #endif | ||||||
|  | #endif // !defined(_Py_OPAQUE_PYOBJECT)
 | ||||||
| 
 | 
 | ||||||
| 
 | 
 | ||||||
| /*
 | /*
 | ||||||
|  |  | ||||||
|  | @ -117,6 +117,7 @@ PyAPI_FUNC(Py_ssize_t) Py_REFCNT(PyObject *ob); | ||||||
|     #endif |     #endif | ||||||
| #endif | #endif | ||||||
| 
 | 
 | ||||||
|  | #ifndef _Py_OPAQUE_PYOBJECT | ||||||
| static inline Py_ALWAYS_INLINE int _Py_IsImmortal(PyObject *op) | static inline Py_ALWAYS_INLINE int _Py_IsImmortal(PyObject *op) | ||||||
| { | { | ||||||
| #if defined(Py_GIL_DISABLED) | #if defined(Py_GIL_DISABLED) | ||||||
|  | @ -140,6 +141,7 @@ static inline Py_ALWAYS_INLINE int _Py_IsStaticImmortal(PyObject *op) | ||||||
| #endif | #endif | ||||||
| } | } | ||||||
| #define _Py_IsStaticImmortal(op) _Py_IsStaticImmortal(_PyObject_CAST(op)) | #define _Py_IsStaticImmortal(op) _Py_IsStaticImmortal(_PyObject_CAST(op)) | ||||||
|  | #endif // !defined(_Py_OPAQUE_PYOBJECT)
 | ||||||
| 
 | 
 | ||||||
| // Py_SET_REFCNT() implementation for stable ABI
 | // Py_SET_REFCNT() implementation for stable ABI
 | ||||||
| PyAPI_FUNC(void) _Py_SetRefcnt(PyObject *ob, Py_ssize_t refcnt); | PyAPI_FUNC(void) _Py_SetRefcnt(PyObject *ob, Py_ssize_t refcnt); | ||||||
|  |  | ||||||
|  | @ -12,7 +12,10 @@ | ||||||
| from test import support | from test import support | ||||||
| 
 | 
 | ||||||
| 
 | 
 | ||||||
| SOURCE = os.path.join(os.path.dirname(__file__), 'extension.c') | SOURCES = [ | ||||||
|  |     os.path.join(os.path.dirname(__file__), 'extension.c'), | ||||||
|  |     os.path.join(os.path.dirname(__file__), 'create_moduledef.c'), | ||||||
|  | ] | ||||||
| SETUP = os.path.join(os.path.dirname(__file__), 'setup.py') | SETUP = os.path.join(os.path.dirname(__file__), 'setup.py') | ||||||
| 
 | 
 | ||||||
| 
 | 
 | ||||||
|  | @ -35,17 +38,22 @@ class BaseTests: | ||||||
|     def test_build(self): |     def test_build(self): | ||||||
|         self.check_build('_test_cext') |         self.check_build('_test_cext') | ||||||
| 
 | 
 | ||||||
|     def check_build(self, extension_name, std=None, limited=False): |     def check_build(self, extension_name, std=None, limited=False, | ||||||
|  |                     opaque_pyobject=False): | ||||||
|         venv_dir = 'env' |         venv_dir = 'env' | ||||||
|         with support.setup_venv_with_pip_setuptools(venv_dir) as python_exe: |         with support.setup_venv_with_pip_setuptools(venv_dir) as python_exe: | ||||||
|             self._check_build(extension_name, python_exe, |             self._check_build(extension_name, python_exe, | ||||||
|                               std=std, limited=limited) |                               std=std, limited=limited, | ||||||
|  |                               opaque_pyobject=opaque_pyobject) | ||||||
| 
 | 
 | ||||||
|     def _check_build(self, extension_name, python_exe, std, limited): |     def _check_build(self, extension_name, python_exe, std, limited, | ||||||
|  |                      opaque_pyobject): | ||||||
|         pkg_dir = 'pkg' |         pkg_dir = 'pkg' | ||||||
|         os.mkdir(pkg_dir) |         os.mkdir(pkg_dir) | ||||||
|         shutil.copy(SETUP, os.path.join(pkg_dir, os.path.basename(SETUP))) |         shutil.copy(SETUP, os.path.join(pkg_dir, os.path.basename(SETUP))) | ||||||
|         shutil.copy(SOURCE, os.path.join(pkg_dir, os.path.basename(SOURCE))) |         for source in SOURCES: | ||||||
|  |             dest = os.path.join(pkg_dir, os.path.basename(source)) | ||||||
|  |             shutil.copy(source, dest) | ||||||
| 
 | 
 | ||||||
|         def run_cmd(operation, cmd): |         def run_cmd(operation, cmd): | ||||||
|             env = os.environ.copy() |             env = os.environ.copy() | ||||||
|  | @ -53,6 +61,8 @@ def run_cmd(operation, cmd): | ||||||
|                 env['CPYTHON_TEST_STD'] = std |                 env['CPYTHON_TEST_STD'] = std | ||||||
|             if limited: |             if limited: | ||||||
|                 env['CPYTHON_TEST_LIMITED'] = '1' |                 env['CPYTHON_TEST_LIMITED'] = '1' | ||||||
|  |             if opaque_pyobject: | ||||||
|  |                 env['CPYTHON_TEST_OPAQUE_PYOBJECT'] = '1' | ||||||
|             env['CPYTHON_TEST_EXT_NAME'] = extension_name |             env['CPYTHON_TEST_EXT_NAME'] = extension_name | ||||||
|             env['TEST_INTERNAL_C_API'] = str(int(self.TEST_INTERNAL_C_API)) |             env['TEST_INTERNAL_C_API'] = str(int(self.TEST_INTERNAL_C_API)) | ||||||
|             if support.verbose: |             if support.verbose: | ||||||
|  | @ -107,6 +117,11 @@ def test_build_limited_c11(self): | ||||||
|     def test_build_c11(self): |     def test_build_c11(self): | ||||||
|         self.check_build('_test_c11_cext', std='c11') |         self.check_build('_test_c11_cext', std='c11') | ||||||
| 
 | 
 | ||||||
|  |     def test_build_opaque_pyobject(self): | ||||||
|  |         # Test with _Py_OPAQUE_PYOBJECT | ||||||
|  |         self.check_build('_test_limited_opaque_cext', limited=True, | ||||||
|  |                          opaque_pyobject=True) | ||||||
|  | 
 | ||||||
|     @unittest.skipIf(support.MS_WINDOWS, "MSVC doesn't support /std:c99") |     @unittest.skipIf(support.MS_WINDOWS, "MSVC doesn't support /std:c99") | ||||||
|     def test_build_c99(self): |     def test_build_c99(self): | ||||||
|         # In public docs, we say C API is compatible with C11. However, |         # In public docs, we say C API is compatible with C11. However, | ||||||
|  |  | ||||||
							
								
								
									
										29
									
								
								Lib/test/test_cext/create_moduledef.c
									
										
									
									
									
										Normal file
									
								
							
							
						
						
									
										29
									
								
								Lib/test/test_cext/create_moduledef.c
									
										
									
									
									
										Normal file
									
								
							|  | @ -0,0 +1,29 @@ | ||||||
|  | // Workaround for testing _Py_OPAQUE_PYOBJECT.
 | ||||||
|  | // See end of 'extension.c'
 | ||||||
|  | 
 | ||||||
|  | 
 | ||||||
|  | #undef _Py_OPAQUE_PYOBJECT | ||||||
|  | #undef Py_LIMITED_API | ||||||
|  | #include "Python.h" | ||||||
|  | 
 | ||||||
|  | 
 | ||||||
|  | // (repeated definition to avoid creating a header)
 | ||||||
|  | extern PyObject *testcext_create_moduledef( | ||||||
|  |     const char *name, const char *doc, | ||||||
|  |     PyMethodDef *methods, PyModuleDef_Slot *slots); | ||||||
|  | 
 | ||||||
|  | PyObject *testcext_create_moduledef( | ||||||
|  |     const char *name, const char *doc, | ||||||
|  |     PyMethodDef *methods, PyModuleDef_Slot *slots) { | ||||||
|  | 
 | ||||||
|  |     static struct PyModuleDef _testcext_module = { | ||||||
|  |         PyModuleDef_HEAD_INIT, | ||||||
|  |     }; | ||||||
|  |     if (!_testcext_module.m_name) { | ||||||
|  |         _testcext_module.m_name = name; | ||||||
|  |         _testcext_module.m_doc = doc; | ||||||
|  |         _testcext_module.m_methods = methods; | ||||||
|  |         _testcext_module.m_slots = slots; | ||||||
|  |     } | ||||||
|  |     return PyModuleDef_Init(&_testcext_module); | ||||||
|  | } | ||||||
|  | @ -78,6 +78,9 @@ _testcext_exec( | ||||||
|     return 0; |     return 0; | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
|  | #define _FUNC_NAME(NAME) PyInit_ ## NAME | ||||||
|  | #define FUNC_NAME(NAME) _FUNC_NAME(NAME) | ||||||
|  | 
 | ||||||
| // Converting from function pointer to void* has undefined behavior, but
 | // Converting from function pointer to void* has undefined behavior, but
 | ||||||
| // works on all known platforms, and CPython's module and type slots currently
 | // works on all known platforms, and CPython's module and type slots currently
 | ||||||
| // need it.
 | // need it.
 | ||||||
|  | @ -96,9 +99,10 @@ static PyModuleDef_Slot _testcext_slots[] = { | ||||||
| 
 | 
 | ||||||
| _Py_COMP_DIAG_POP | _Py_COMP_DIAG_POP | ||||||
| 
 | 
 | ||||||
| 
 |  | ||||||
| PyDoc_STRVAR(_testcext_doc, "C test extension."); | PyDoc_STRVAR(_testcext_doc, "C test extension."); | ||||||
| 
 | 
 | ||||||
|  | #ifndef _Py_OPAQUE_PYOBJECT | ||||||
|  | 
 | ||||||
| static struct PyModuleDef _testcext_module = { | static struct PyModuleDef _testcext_module = { | ||||||
|     PyModuleDef_HEAD_INIT,  // m_base
 |     PyModuleDef_HEAD_INIT,  // m_base
 | ||||||
|     STR(MODULE_NAME),  // m_name
 |     STR(MODULE_NAME),  // m_name
 | ||||||
|  | @ -112,11 +116,30 @@ static struct PyModuleDef _testcext_module = { | ||||||
| }; | }; | ||||||
| 
 | 
 | ||||||
| 
 | 
 | ||||||
| #define _FUNC_NAME(NAME) PyInit_ ## NAME |  | ||||||
| #define FUNC_NAME(NAME) _FUNC_NAME(NAME) |  | ||||||
| 
 |  | ||||||
| PyMODINIT_FUNC | PyMODINIT_FUNC | ||||||
| FUNC_NAME(MODULE_NAME)(void) | FUNC_NAME(MODULE_NAME)(void) | ||||||
| { | { | ||||||
|     return PyModuleDef_Init(&_testcext_module); |     return PyModuleDef_Init(&_testcext_module); | ||||||
| } | } | ||||||
|  | 
 | ||||||
|  | #else  // _Py_OPAQUE_PYOBJECT
 | ||||||
|  | 
 | ||||||
|  | // Opaque PyObject means that PyModuleDef is also opaque and cannot be
 | ||||||
|  | // declared statically. See PEP 793.
 | ||||||
|  | // So, this part of module creation is split into a separate source file
 | ||||||
|  | // which uses non-limited API.
 | ||||||
|  | 
 | ||||||
|  | // (repeated definition to avoid creating a header)
 | ||||||
|  | extern PyObject *testcext_create_moduledef( | ||||||
|  |     const char *name, const char *doc, | ||||||
|  |     PyMethodDef *methods, PyModuleDef_Slot *slots); | ||||||
|  | 
 | ||||||
|  | 
 | ||||||
|  | PyMODINIT_FUNC | ||||||
|  | FUNC_NAME(MODULE_NAME)(void) | ||||||
|  | { | ||||||
|  |     return testcext_create_moduledef( | ||||||
|  |         STR(MODULE_NAME), _testcext_doc, _testcext_methods, _testcext_slots); | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | #endif  // _Py_OPAQUE_PYOBJECT
 | ||||||
|  |  | ||||||
|  | @ -59,8 +59,11 @@ def main(): | ||||||
|     std = os.environ.get("CPYTHON_TEST_STD", "") |     std = os.environ.get("CPYTHON_TEST_STD", "") | ||||||
|     module_name = os.environ["CPYTHON_TEST_EXT_NAME"] |     module_name = os.environ["CPYTHON_TEST_EXT_NAME"] | ||||||
|     limited = bool(os.environ.get("CPYTHON_TEST_LIMITED", "")) |     limited = bool(os.environ.get("CPYTHON_TEST_LIMITED", "")) | ||||||
|  |     opaque_pyobject = bool(os.environ.get("CPYTHON_TEST_OPAQUE_PYOBJECT", "")) | ||||||
|     internal = bool(int(os.environ.get("TEST_INTERNAL_C_API", "0"))) |     internal = bool(int(os.environ.get("TEST_INTERNAL_C_API", "0"))) | ||||||
| 
 | 
 | ||||||
|  |     sources = [SOURCE] | ||||||
|  | 
 | ||||||
|     if not internal: |     if not internal: | ||||||
|         cflags = list(PUBLIC_CFLAGS) |         cflags = list(PUBLIC_CFLAGS) | ||||||
|     else: |     else: | ||||||
|  | @ -93,6 +96,11 @@ def main(): | ||||||
|         version = sys.hexversion |         version = sys.hexversion | ||||||
|         cflags.append(f'-DPy_LIMITED_API={version:#x}') |         cflags.append(f'-DPy_LIMITED_API={version:#x}') | ||||||
| 
 | 
 | ||||||
|  |     # Define _Py_OPAQUE_PYOBJECT macro | ||||||
|  |     if opaque_pyobject: | ||||||
|  |         cflags.append(f'-D_Py_OPAQUE_PYOBJECT') | ||||||
|  |         sources.append('create_moduledef.c') | ||||||
|  | 
 | ||||||
|     if internal: |     if internal: | ||||||
|         cflags.append('-DTEST_INTERNAL_C_API=1') |         cflags.append('-DTEST_INTERNAL_C_API=1') | ||||||
| 
 | 
 | ||||||
|  | @ -120,7 +128,7 @@ def main(): | ||||||
| 
 | 
 | ||||||
|     ext = Extension( |     ext = Extension( | ||||||
|         module_name, |         module_name, | ||||||
|         sources=[SOURCE], |         sources=sources, | ||||||
|         extra_compile_args=cflags, |         extra_compile_args=cflags, | ||||||
|         include_dirs=include_dirs, |         include_dirs=include_dirs, | ||||||
|         library_dirs=library_dirs) |         library_dirs=library_dirs) | ||||||
|  |  | ||||||
		Loading…
	
	Add table
		Add a link
		
	
		Reference in a new issue
	
	 Petr Viktorin
						Petr Viktorin