From b173f7853e4e3a4215a661d98174291e379cf6fb Mon Sep 17 00:00:00 2001 From: Benjamin Peterson Date: Tue, 5 May 2009 22:31:58 +0000 Subject: add a replacement API for PyCObject, PyCapsule #5630 All stdlib modules with C-APIs now use this. Patch by Larry Hastings --- Doc/c-api/capsule.rst | 168 +++++++++++++++++++ Doc/c-api/cobject.rst | 5 +- Doc/c-api/concrete.rst | 1 + Doc/data/refcounts.dat | 39 +++++ Doc/extending/extending.rst | 62 ++++--- Include/Python.h | 1 + Include/cobject.h | 8 +- Include/datetime.h | 13 +- Include/py_curses.h | 15 +- Include/pycapsule.h | 57 +++++++ Include/pyexpat.h | 1 + Include/ucnhash.h | 4 +- Lib/test/test_sys.py | 2 +- Makefile.pre.in | 2 + Misc/NEWS | 5 + Modules/_ctypes/callproc.c | 16 +- Modules/_ctypes/cfield.c | 14 +- Modules/_cursesmodule.c | 7 +- Modules/_elementtree.c | 2 +- Modules/_ssl.c | 7 +- Modules/_testcapimodule.c | 152 ++++++++++++++++- Modules/cjkcodecs/cjkcodecs.h | 12 +- Modules/cjkcodecs/multibytecodec.c | 4 +- Modules/cjkcodecs/multibytecodec.h | 3 + Modules/datetimemodule.c | 9 +- Modules/pyexpat.c | 4 +- Modules/socketmodule.c | 10 +- Modules/socketmodule.h | 45 +----- Modules/unicodedata.c | 2 +- Objects/capsule.c | 324 +++++++++++++++++++++++++++++++++++++ Objects/object.c | 5 +- Objects/unicodeobject.c | 11 +- PC/os2emx/python27.def | 13 ++ PC/os2vacpp/python.def | 14 +- PCbuild/pythoncore.vcproj | 5 + Python/compile.c | 16 +- Python/getargs.c | 34 +++- 37 files changed, 943 insertions(+), 149 deletions(-) create mode 100644 Doc/c-api/capsule.rst create mode 100644 Include/pycapsule.h create mode 100644 Objects/capsule.c diff --git a/Doc/c-api/capsule.rst b/Doc/c-api/capsule.rst new file mode 100644 index 0000000..163ad60 --- /dev/null +++ b/Doc/c-api/capsule.rst @@ -0,0 +1,168 @@ +.. highlightlang:: c + +.. _capsules: + +Capsules +-------- + +.. index:: object: Capsule + +Refer to :ref:`using-capsules` for more information on using these objects. + + +.. ctype:: PyCapsule + + This subtype of :ctype:`PyObject` represents an opaque value, useful for C + extension modules who need to pass an opaque value (as a :ctype:`void\*` + pointer) through Python code to other C code. It is often used to make a C + function pointer defined in one module available to other modules, so the + regular import mechanism can be used to access C APIs defined in dynamically + loaded modules. + +.. ctype:: PyCapsule_Destructor + + The type of a destructor callback for a capsule. Defined as:: + + typedef void (*PyCapsule_Destructor)(PyObject *); + + See :cfunc:`PyCapsule_New` for the semantics of PyCapsule_Destructor + callbacks. + + +.. cfunction:: int PyCapsule_CheckExact(PyObject *p) + + Return true if its argument is a :ctype:`PyCapsule`. + +.. cfunction:: PyObject* PyCapsule_New(void* pointer, const char* name, PyCapsule_Destructor destructor) + + Create a :ctype:`PyCapsule` encapsulating the *pointer*. The *pointer* + argument may not be *NULL*. + + The *name* string may either be *NULL* or a pointer to a valid + C string. If non-*NULL*, this string must outlive the capsule. + (Though it is permitted to free it inside the *destructor*.) + + If the *destructor* argument is not *NULL*, + it will be called with the capsule ``PyObject *`` when it is destroyed. + + If this capsule will be stored as an attribute of a module, it + is strongly suggested that the *name* string be specified as:: + + modulename.attributename + + This will enable other modules to import the capsule + using :cfunc:`PyCapsule_Import`. + + Return a valid capsule on success. + On failure, set an exception and return *NULL*. + + +.. cfunction:: void* PyCapsule_GetPointer(PyObject* capsule, const char* name) + + Retrieve the *pointer* stored in the capsule. + + The *name* parameter must compare exactly to the name stored in the capsule. + If the name stored in the capsule is *NULL*, the *name* passed in must + also be *NULL*. If the name stored in the capsule is non-*NULL*, + the *name* passed in must also be non-*NULL*, and must match the name + stored in the capsule. Python uses the C function *strcmp* to compare + capsule names. + + Return the internal *pointer* on success. + On failure, set an exception and return *NULL*. + + +.. cfunction:: PyCapsule_Destructor PyCapsule_GetDestructor(PyObject* capsule) + + Return the current *destructor* stored in the capsule. + On failure, set an exception and return *NULL*. + + It is legal for a capsule to have a *NULL* destructor. + This makes a *NULL* return code somewhat ambiguous; + use :cfunc:`PyCapsule_IsValid` or :cfunc:`PyErr_Occurred` to disambugate. + + +.. cfunction:: void* PyCapsule_GetContext(PyObject* capsule) + + Return the current *context* stored in the capsule. + On failure, set an exception and return *NULL*. + + It is legal for a capsule to have a *NULL* context. + This makes a *NULL* return code somewhat ambiguous; + use :cfunc:`PyCapsule_IsValid` or :cfunc:`PyErr_Occurred` to disambugate. + + +.. cfunction:: const char* PyCapsule_GetName(PyObject* capsule) + + Return the current *name* stored in the capsule. + On failure, set an exception and return *NULL*. + + It is legal for a capsule to have a *NULL* name. + This makes a *NULL* return code somewhat ambiguous; + use :cfunc:`PyCapsule_IsValid` or :cfunc:`PyErr_Occurred` to disambugate. + + +.. cfunction:: void* PyCapsule_Import(const char* name, int no_block) + + Import a pointer to a C object from a ``capsule`` attribute in a module. + The *name* parameter should specify the full name to the attribute, as + in *"module.attribute"*. + The *name* stored in the capsule must match this string exactly. + If *no_block* is true, import the module without blocking + (using :cfunc:`PyImport_ImportModuleNoBlock`). + If *no_block* is false, import the module conventionally + (using :cfunc:`PyImport_ImportModule`). + + Return the capsule's internal *pointer* on success. + On failure, set an exception and return *NULL*. + Exception: if *PyCapsule_Import* failed to import the module, + and *no_block* was true, no exception is set. + +.. cfunction:: int PyCapsule_IsValid(PyObject* capsule, const char* name) + + Determines whether or not a :ctype:`PyObject \*` is a valid capsule. + A valid capsule is non-*NULL*, passes :cfunc:`PyCapsule_CheckExact`, + has a non-NULL *pointer*, and its internal name matches the + *name* parameter. (See :cfunc:`PyCapsule_GetPointer` for + information on how capsule names are compared.) + + In other words, if :cfunc:`PyCapsule_IsValid` returns a true value, + calls to any of the accessors (any function starting + with :cfunc:`PyCapsule_Get`) are guaranteed to succeed. + + Return a nonzero value if the object is valid and matches the name + passed in. + Return 0 otherwise. + This function will not fail. + +.. cfunction:: int PyCapsule_SetContext(PyObject* capsule, void* context) + + Set the context pointer inside *capsule* to *context*. + + Return 0 on success. + Return nonzero and set an exception on failure. + +.. cfunction:: int PyCapsule_SetDestructor(PyObject* capsule, void (*)(PyObject *) destructor) + + Set the destructor inside *capsule* to *destructor*. + + Return 0 on success. + Return nonzero and set an exception on failure. + +.. cfunction:: int PyCapsule_SetName(PyObject* capsule, const char* name) + + Set the name inside *capsule* to *name*. If non-*NULL*, the name + must outlive the capsule. If the previous *name* stored in the + capsule was not *NULL*, no attempt is made to free it. + + Return 0 on success. + Return nonzero and set an exception on failure. + +.. cfunction:: int PyCapsule_SetPointer(PyObject* capsule, void* pointer) + + Set the void pointer inside *capsule* to *pointer*. The pointer + may not be *NULL*. + + Return 0 on success. + Return nonzero and set an exception on failure. + diff --git a/Doc/c-api/cobject.rst b/Doc/c-api/cobject.rst index 10f7bba..ee65a98 100644 --- a/Doc/c-api/cobject.rst +++ b/Doc/c-api/cobject.rst @@ -7,8 +7,11 @@ CObjects .. index:: object: CObject -Refer to :ref:`using-cobjects` for more information on using these objects. +.. warning:: + + The CObject API is deprecated as of Python 3.1. Please switch to the new + :ref:`capsules` API. .. ctype:: PyCObject diff --git a/Doc/c-api/concrete.rst b/Doc/c-api/concrete.rst index 2ba0833..d1fdec0 100644 --- a/Doc/c-api/concrete.rst +++ b/Doc/c-api/concrete.rst @@ -101,6 +101,7 @@ Other Objects descriptor.rst slice.rst weakref.rst + capsule.rst cobject.rst cell.rst gen.rst diff --git a/Doc/data/refcounts.dat b/Doc/data/refcounts.dat index a0e7e0c..400cf64 100644 --- a/Doc/data/refcounts.dat +++ b/Doc/data/refcounts.dat @@ -55,6 +55,45 @@ PyBuffer_FromReadWriteMemory:int:size:: PyBuffer_New:PyObject*::+1: PyBuffer_New:int:size:: +PyCapsule_GetContext:void *::: +PyCapsule_GetContext:PyObject*:self:0: + +PyCapsule_GetDestructor:void (*)(PyObject *)::: +PyCapsule_GetDestructor:PyObject*:self:0: + +PyCapsule_GetName:const char *::: +PyCapsule_GetName:PyObject*:self:0: + +PyCapsule_GetPointer:void*::: +PyCapsule_GetPointer:PyObject*:self:0: +PyCapsule_GetPointer:const char *:name:: + +PyCapsule_Import:void *::: +PyCapsule_Import:const char *:name:: +PyCapsule_Import:int:no_block:: + +PyCapsule_New:PyObject*::+1: +PyCapsule_New:void*:pointer:: +PyCapsule_New:const char *:name:: +PyCapsule_New::void (* destructor)(PyObject* ):: + +PyCapsule_SetContext:int::: +PyCapsule_SetContext:PyObject*:self:0: +PyCapsule_SetContext:void *:context:: + +PyCapsule_SetDestructor:int::: +PyCapsule_SetDestructor:PyObject*:self:0: +PyCapsule_SetDestructor:void (*)(PyObject *):destructor:: + +PyCapsule_SetName:int::: +PyCapsule_SetName:PyObject*:self:0: +PyCapsule_SetName:const char *:name:: + +PyCapsule_SetPointer:int::: +PyCapsule_SetPointer:PyObject*:self:0: +PyCapsule_SetPointer:void*:pointer:: + + PyCObject_AsVoidPtr:void*::: PyCObject_AsVoidPtr:PyObject*:self:0: diff --git a/Doc/extending/extending.rst b/Doc/extending/extending.rst index 82cc40b..7e680db 100644 --- a/Doc/extending/extending.rst +++ b/Doc/extending/extending.rst @@ -1075,7 +1075,7 @@ already if the symbol ``__cplusplus`` is defined (all recent C++ compilers define this symbol). -.. _using-cobjects: +.. _using-capsules: Providing a C API for an Extension Module ========================================= @@ -1111,23 +1111,40 @@ avoid name clashes with other extension modules (as discussed in section other extension modules must be exported in a different way. Python provides a special mechanism to pass C-level information (pointers) from -one extension module to another one: CObjects. A CObject is a Python data type -which stores a pointer (:ctype:`void \*`). CObjects can only be created and +one extension module to another one: Capsules. A Capsule is a Python data type +which stores a pointer (:ctype:`void \*`). Capsules can only be created and accessed via their C API, but they can be passed around like any other Python object. In particular, they can be assigned to a name in an extension module's namespace. Other extension modules can then import this module, retrieve the -value of this name, and then retrieve the pointer from the CObject. +value of this name, and then retrieve the pointer from the Capsule. -There are many ways in which CObjects can be used to export the C API of an -extension module. Each name could get its own CObject, or all C API pointers -could be stored in an array whose address is published in a CObject. And the +There are many ways in which Capsules can be used to export the C API of an +extension module. Each function could get its own Capsule, or all C API pointers +could be stored in an array whose address is published in a Capsule. And the various tasks of storing and retrieving the pointers can be distributed in different ways between the module providing the code and the client modules. +Whichever method you choose, it's important to name your Capsules properly. +The function :cfunc:`PyCapsule_New` takes a name parameter +(:ctype:`const char \*`); you're permitted to pass in a *NULL* name, but +we strongly encourage you to specify a name. Properly named Capsules provide +a degree of runtime type-safety; there is no feasible way to tell one unnamed +Capsule from another. + +In particular, Capsules used to expose C APIs should be given a name following +this convention:: + + modulename.attributename + +The convenience function :cfunc:`PyCapsule_Import` makes it easy to +load a C API provided via a Capsule, but only if the Capsule's name +matches this convention. This behavior gives C API users a high degree +of certainty that the Capsule they load contains the correct C API. + The following example demonstrates an approach that puts most of the burden on the writer of the exporting module, which is appropriate for commonly used library modules. It stores all C API pointers (just one in the example!) in an -array of :ctype:`void` pointers which becomes the value of a CObject. The header +array of :ctype:`void` pointers which becomes the value of a Capsule. The header file corresponding to the module provides a macro that takes care of importing the module and retrieving its C API pointers; client modules only have to call this macro before accessing the C API. @@ -1189,8 +1206,8 @@ function must take care of initializing the C API pointer array:: /* Initialize the C API pointer array */ PySpam_API[PySpam_System_NUM] = (void *)PySpam_System; - /* Create a CObject containing the API pointer array's address */ - c_api_object = PyCObject_FromVoidPtr((void *)PySpam_API, NULL); + /* Create a Capsule containing the API pointer array's address */ + c_api_object = PyCapsule_New((void *)PySpam_API, "spam._C_API", NULL); if (c_api_object != NULL) PyModule_AddObject(m, "_C_API", c_api_object); @@ -1233,21 +1250,14 @@ like this:: #define PySpam_System \ (*(PySpam_System_RETURN (*)PySpam_System_PROTO) PySpam_API[PySpam_System_NUM]) - /* Return -1 and set exception on error, 0 on success. */ + /* Return -1 on error, 0 on success. + * PyCapsule_Import will set an exception if there's an error. + */ static int import_spam(void) { - PyObject *module = PyImport_ImportModule("spam"); - - if (module != NULL) { - PyObject *c_api_object = PyObject_GetAttrString(module, "_C_API"); - if (c_api_object == NULL) - return -1; - if (PyCObject_Check(c_api_object)) - PySpam_API = (void **)PyCObject_AsVoidPtr(c_api_object); - Py_DECREF(c_api_object); - } - return 0; + PySpam_API = (void **)PyCapsule_Import("spam._C_API", 0); + return (PySpam_API != NULL) ? 0 : -1; } #endif @@ -1280,11 +1290,11 @@ The main disadvantage of this approach is that the file :file:`spammodule.h` is rather complicated. However, the basic structure is the same for each function that is exported, so it has to be learned only once. -Finally it should be mentioned that CObjects offer additional functionality, +Finally it should be mentioned that Capsules offer additional functionality, which is especially useful for memory allocation and deallocation of the pointer -stored in a CObject. The details are described in the Python/C API Reference -Manual in the section :ref:`cobjects` and in the implementation of CObjects (files -:file:`Include/cobject.h` and :file:`Objects/cobject.c` in the Python source +stored in a Capsule. The details are described in the Python/C API Reference +Manual in the section :ref:`capsules` and in the implementation of Capsules (files +:file:`Include/pycapsule.h` and :file:`Objects/pycapsule.c` in the Python source code distribution). .. rubric:: Footnotes diff --git a/Include/Python.h b/Include/Python.h index 54058a5..969ef0f 100644 --- a/Include/Python.h +++ b/Include/Python.h @@ -89,6 +89,7 @@ #include "classobject.h" #include "fileobject.h" #include "cobject.h" +#include "pycapsule.h" #include "traceback.h" #include "sliceobject.h" #include "cellobject.h" diff --git a/Include/cobject.h b/Include/cobject.h index 4e24d7d..9efe4ce 100644 --- a/Include/cobject.h +++ b/Include/cobject.h @@ -1,10 +1,8 @@ -/* C objects to be exported from one extension module to another. +/* - C objects are used for communication between extension modules. - They provide a way for an extension module to export a C interface - to other extension modules, so that extension modules can use the - Python import mechanism to link to one another. +The CObject module is now *deprecated* as of Python 3.1. +Please use the Capsule API instead; see "pycapsule.h". */ diff --git a/Include/datetime.h b/Include/datetime.h index 0d310b4..4b506b2 100644 --- a/Include/datetime.h +++ b/Include/datetime.h @@ -158,9 +158,8 @@ typedef struct { } PyDateTime_CAPI; +#define PyDateTime_CAPSULE_NAME "datetime.datetime_CAPI" -/* "magic" constant used to partially protect against developer mistakes. */ -#define DATETIME_API_MAGIC 0x414548d5 #ifdef Py_BUILD_CORE @@ -186,15 +185,7 @@ typedef struct { static PyDateTime_CAPI *PyDateTimeAPI; #define PyDateTime_IMPORT \ - PyDateTimeAPI = (PyDateTime_CAPI*) PyCObject_Import("datetime", \ - "datetime_CAPI") - -/* This macro would be used if PyCObject_ImportEx() was created. -#define PyDateTime_IMPORT \ - PyDateTimeAPI = (PyDateTime_CAPI*) PyCObject_ImportEx("datetime", \ - "datetime_CAPI", \ - DATETIME_API_MAGIC) -*/ + PyDateTimeAPI = (PyDateTime_CAPI *)PyCapsule_Import(PyDateTime_CAPSULE_NAME, 0) /* Macros for type checking when not building the Python core. */ #define PyDate_Check(op) PyObject_TypeCheck(op, PyDateTimeAPI->DateType) diff --git a/Include/py_curses.h b/Include/py_curses.h index b4ad8f7..ae7b12b 100644 --- a/Include/py_curses.h +++ b/Include/py_curses.h @@ -75,6 +75,9 @@ typedef struct { #define PyCursesWindow_Check(v) (Py_TYPE(v) == &PyCursesWindow_Type) +#define PyCurses_CAPSULE_NAME "_curses._C_API" + + #ifdef CURSES_MODULE /* This section is used when compiling _cursesmodule.c */ @@ -89,16 +92,8 @@ static void **PyCurses_API; #define PyCursesInitialisedColor {if (! ((int (*)(void))PyCurses_API[3]) () ) return NULL;} #define import_curses() \ -{ \ - PyObject *module = PyImport_ImportModuleNoBlock("_curses"); \ - if (module != NULL) { \ - PyObject *module_dict = PyModule_GetDict(module); \ - PyObject *c_api_object = PyDict_GetItemString(module_dict, "_C_API"); \ - if (PyCObject_Check(c_api_object)) { \ - PyCurses_API = (void **)PyCObject_AsVoidPtr(c_api_object); \ - } \ - } \ -} + PyCurses_API = (void **)PyCapsule_Import(PyCurses_CAPSULE_NAME, 1); + #endif /* general error messages */ diff --git a/Include/pycapsule.h b/Include/pycapsule.h new file mode 100644 index 0000000..17a509e --- /dev/null +++ b/Include/pycapsule.h @@ -0,0 +1,57 @@ + +/* Capsule objects let you wrap a C "void *" pointer in a Python + object. They're a way of passing data through the Python interpreter + without creating your own custom type. + + Capsules are used for communication between extension modules. + They provide a way for an extension module to export a C interface + to other extension modules, so that extension modules can use the + Python import mechanism to link to one another. + + For more information, please see "c-api/capsule.html" in the + documentation. +*/ + +#ifndef Py_CAPSULE_H +#define Py_CAPSULE_H +#ifdef __cplusplus +extern "C" { +#endif + +PyAPI_DATA(PyTypeObject) PyCapsule_Type; + +typedef void (*PyCapsule_Destructor)(PyObject *); + +#define PyCapsule_CheckExact(op) (Py_TYPE(op) == &PyCapsule_Type) + + +PyAPI_FUNC(PyObject *) PyCapsule_New( + void *pointer, + const char *name, + PyCapsule_Destructor destructor); + +PyAPI_FUNC(void *) PyCapsule_GetPointer(PyObject *capsule, const char *name); + +PyAPI_FUNC(PyCapsule_Destructor) PyCapsule_GetDestructor(PyObject *capsule); + +PyAPI_FUNC(const char *) PyCapsule_GetName(PyObject *capsule); + +PyAPI_FUNC(void *) PyCapsule_GetContext(PyObject *capsule); + +PyAPI_FUNC(int) PyCapsule_IsValid(PyObject *capsule, const char *name); + +PyAPI_FUNC(int) PyCapsule_SetPointer(PyObject *capsule, void *pointer); + +PyAPI_FUNC(int) PyCapsule_SetDestructor(PyObject *capsule, PyCapsule_Destructor destructor); + +PyAPI_FUNC(int) PyCapsule_SetName(PyObject *capsule, const char *name); + +PyAPI_FUNC(int) PyCapsule_SetContext(PyObject *capsule, void *context); + +PyAPI_FUNC(void *) PyCapsule_Import(const char *name, int no_block); + + +#ifdef __cplusplus +} +#endif +#endif /* !Py_CAPSULE_H */ diff --git a/Include/pyexpat.h b/Include/pyexpat.h index 1e79f4e..5340ef5 100644 --- a/Include/pyexpat.h +++ b/Include/pyexpat.h @@ -4,6 +4,7 @@ /* note: you must import expat.h before importing this module! */ #define PyExpat_CAPI_MAGIC "pyexpat.expat_CAPI 1.0" +#define PyExpat_CAPSULE_NAME "pyexpat.expat_CAPI" struct PyExpat_CAPI { diff --git a/Include/ucnhash.h b/Include/ucnhash.h index 6231c98..69b7774 100644 --- a/Include/ucnhash.h +++ b/Include/ucnhash.h @@ -6,7 +6,9 @@ extern "C" { #endif -/* revised ucnhash CAPI interface (exported through a PyCObject) */ +/* revised ucnhash CAPI interface (exported through a "wrapper") */ + +#define PyUnicodeData_CAPSULE_NAME "unicodedata.ucnhash_CAPI" typedef struct { diff --git a/Lib/test/test_sys.py b/Lib/test/test_sys.py index 201f490..8f68cf7 100644 --- a/Lib/test/test_sys.py +++ b/Lib/test/test_sys.py @@ -644,7 +644,7 @@ class SizeofTest(unittest.TestCase): def delx(self): del self.__x x = property(getx, setx, delx, "") check(x, size(h + '4Pi')) - # PyCObject + # PyCapsule # XXX # rangeiterator check(iter(range(1)), size(h + '4l')) diff --git a/Makefile.pre.in b/Makefile.pre.in index b02f517..b2525e2 100644 --- a/Makefile.pre.in +++ b/Makefile.pre.in @@ -344,6 +344,7 @@ OBJECT_OBJS= \ Objects/moduleobject.o \ Objects/object.o \ Objects/obmalloc.o \ + Objects/capsule.o \ Objects/rangeobject.o \ Objects/setobject.o \ Objects/sliceobject.o \ @@ -654,6 +655,7 @@ PYTHON_HEADERS= \ Include/pgen.h \ Include/pgenheaders.h \ Include/pyarena.h \ + Include/pycapsule.h \ Include/pyctype.h \ Include/pydebug.h \ Include/pyerrors.h \ diff --git a/Misc/NEWS b/Misc/NEWS index c0940f4..13360f8 100644 --- a/Misc/NEWS +++ b/Misc/NEWS @@ -226,6 +226,11 @@ Tests support.EnvironmentVarGuard objects restored the environment variables incorrectly on __exit__. +C-API +----- + +- Issue #5630: A replacement PyCObject API, PyCapsule, has been added. + What's New in Python 3.1 alpha 2? ================================= diff --git a/Modules/_ctypes/callproc.c b/Modules/_ctypes/callproc.c index 3300cde..cbc5cf8 100644 --- a/Modules/_ctypes/callproc.c +++ b/Modules/_ctypes/callproc.c @@ -78,6 +78,16 @@ #define DONT_USE_SEH #endif +#define CTYPES_CAPSULE_NAME_PYMEM "_ctypes pymem" + +static void pymem_destructor(PyObject *ptr) +{ + void *p = PyCapsule_GetPointer(ptr, CTYPES_CAPSULE_NAME_PYMEM); + if (p) { + PyMem_Free(p); + } +} + /* ctypes maintains thread-local storage that has space for two error numbers: private copies of the system 'errno' value and, on Windows, the system error code @@ -136,7 +146,7 @@ _ctypes_get_errobj(int **pspace) if (space == NULL) return NULL; memset(space, 0, sizeof(int) * 2); - errobj = PyCObject_FromVoidPtr(space, PyMem_Free); + errobj = PyCapsule_New(space, CTYPES_CAPSULE_NAME_PYMEM, pymem_destructor); if (errobj == NULL) return NULL; if (-1 == PyDict_SetItem(dict, error_object_name, @@ -145,7 +155,7 @@ _ctypes_get_errobj(int **pspace) return NULL; } } - *pspace = (int *)PyCObject_AsVoidPtr(errobj); + *pspace = (int *)PyCapsule_GetPointer(errobj, CTYPES_CAPSULE_NAME_PYMEM); return errobj; } @@ -658,7 +668,7 @@ static int ConvParam(PyObject *obj, Py_ssize_t index, struct argument *pa) return -1; } memset(pa->value.p, 0, size); - pa->keep = PyCObject_FromVoidPtr(pa->value.p, PyMem_Free); + pa->keep = PyCapsule_New(pa->value.p, CTYPES_CAPSULE_NAME_PYMEM, pymem_destructor); if (!pa->keep) { PyMem_Free(pa->value.p); return -1; diff --git a/Modules/_ctypes/cfield.c b/Modules/_ctypes/cfield.c index ff24405..d7966a5 100644 --- a/Modules/_ctypes/cfield.c +++ b/Modules/_ctypes/cfield.c @@ -6,6 +6,18 @@ #endif #include "ctypes.h" + +#define CTYPES_CFIELD_CAPSULE_NAME_PYMEM "_ctypes/cfield.c pymem" + +static void pymem_destructor(PyObject *ptr) +{ + void *p = PyCapsule_GetPointer(ptr, CTYPES_CFIELD_CAPSULE_NAME_PYMEM); + if (p) { + PyMem_Free(p); + } +} + + /******************************************************************/ /* PyCField_Type @@ -1477,7 +1489,7 @@ Z_set(void *ptr, PyObject *value, Py_ssize_t size) return PyErr_NoMemory(); } memset(buffer, 0, size); - keep = PyCObject_FromVoidPtr(buffer, PyMem_Free); + keep = PyCapsule_New(buffer, CTYPES_CFIELD_CAPSULE_NAME_PYMEM, pymem_destructor); if (!keep) { Py_DECREF(value); PyMem_Free(buffer); diff --git a/Modules/_cursesmodule.c b/Modules/_cursesmodule.c index 5cb198e..a92cc8e 100644 --- a/Modules/_cursesmodule.c +++ b/Modules/_cursesmodule.c @@ -104,6 +104,7 @@ char *PyCursesVersion = "2.2"; #include "Python.h" + #ifdef __osf__ #define STRICT_SYSV_CURSES /* Don't use ncurses extensions */ #endif @@ -174,7 +175,7 @@ static int initialisedcolors = FALSE; /* * Check the return code from a curses function and return None * or raise an exception as appropriate. These are exported using the - * CObject API. + * capsule API. */ static PyObject * @@ -2827,8 +2828,8 @@ PyInit__curses(void) return NULL; ModDict = d; /* For PyCurses_InitScr to use later */ - /* Add a CObject for the C API */ - c_api_object = PyCObject_FromVoidPtr((void *)PyCurses_API, NULL); + /* Add a capsule for the C API */ + c_api_object = PyCapsule_New(PyCurses_API, PyCurses_CAPSULE_NAME, NULL); PyDict_SetItemString(d, "_C_API", c_api_object); Py_DECREF(c_api_object); diff --git a/Modules/_elementtree.c b/Modules/_elementtree.c index ac118b4..ae09893 100644 --- a/Modules/_elementtree.c +++ b/Modules/_elementtree.c @@ -2809,7 +2809,7 @@ PyInit__elementtree(void) #if defined(USE_PYEXPAT_CAPI) /* link against pyexpat, if possible */ - capi = PyCObject_Import("pyexpat", "expat_CAPI"); + capi = PyCapsule_Import(PyExpat_CAPSULE_NAME, 0); if (capi && strcmp(capi->magic, PyExpat_CAPI_MAGIC) == 0 && capi->size <= sizeof(*expat_capi) && diff --git a/Modules/_ssl.c b/Modules/_ssl.c index 3c9dd61..b400057 100644 --- a/Modules/_ssl.c +++ b/Modules/_ssl.c @@ -71,6 +71,8 @@ enum py_ssl_version { /* Include symbols from _socket module */ #include "socketmodule.h" +static PySocketModule_APIObject PySocketModule; + #if defined(HAVE_POLL_H) #include #elif defined(HAVE_SYS_POLL_H) @@ -1626,6 +1628,7 @@ PyMODINIT_FUNC PyInit__ssl(void) { PyObject *m, *d; + PySocketModule_APIObject *socket_api; if (PyType_Ready(&PySSL_Type) < 0) return NULL; @@ -1636,8 +1639,10 @@ PyInit__ssl(void) d = PyModule_GetDict(m); /* Load _socket module and its C API */ - if (PySocketModule_ImportModuleAndAPI()) + socket_api = PySocketModule_ImportModuleAndAPI(); + if (!socket_api) return NULL; + PySocketModule = *socket_api; /* Init OpenSSL */ SSL_load_error_strings(); diff --git a/Modules/_testcapimodule.c b/Modules/_testcapimodule.c index 3f7190c..f3899f5 100644 --- a/Modules/_testcapimodule.c +++ b/Modules/_testcapimodule.c @@ -1102,6 +1102,155 @@ test_string_to_double(PyObject *self) { } +/* Coverage testing of capsule objects. */ + +static const char *capsule_name = "capsule name"; +static char *capsule_pointer = "capsule pointer"; +static char *capsule_context = "capsule context"; +static const char *capsule_error = NULL; +static int +capsule_destructor_call_count = 0; + +static void +capsule_destructor(PyObject *o) { + capsule_destructor_call_count++; + if (PyCapsule_GetContext(o) != capsule_context) { + capsule_error = "context did not match in destructor!"; + } else if (PyCapsule_GetDestructor(o) != capsule_destructor) { + capsule_error = "destructor did not match in destructor! (woah!)"; + } else if (PyCapsule_GetName(o) != capsule_name) { + capsule_error = "name did not match in destructor!"; + } else if (PyCapsule_GetPointer(o, capsule_name) != capsule_pointer) { + capsule_error = "pointer did not match in destructor!"; + } +} + +typedef struct { + char *name; + char *module; + char *attribute; +} known_capsule; + +static PyObject * +test_capsule(PyObject *self, PyObject *args) +{ + PyObject *object; + const char *error = NULL; + void *pointer; + void *pointer2; + known_capsule known_capsules[] = { + #define KNOWN_CAPSULE(module, name) { module "." name, module, name } + KNOWN_CAPSULE("_socket", "CAPI"), + KNOWN_CAPSULE("_curses", "_C_API"), + KNOWN_CAPSULE("datetime", "datetime_CAPI"), + { NULL, NULL }, + }; + known_capsule *known = &known_capsules[0]; + +#define FAIL(x) { error = (x); goto exit; } + +#define CHECK_DESTRUCTOR \ + if (capsule_error) { \ + FAIL(capsule_error); \ + } \ + else if (!capsule_destructor_call_count) { \ + FAIL("destructor not called!"); \ + } \ + capsule_destructor_call_count = 0; \ + + object = PyCapsule_New(capsule_pointer, capsule_name, capsule_destructor); + PyCapsule_SetContext(object, capsule_context); + capsule_destructor(object); + CHECK_DESTRUCTOR; + Py_DECREF(object); + CHECK_DESTRUCTOR; + + object = PyCapsule_New(known, "ignored", NULL); + PyCapsule_SetPointer(object, capsule_pointer); + PyCapsule_SetName(object, capsule_name); + PyCapsule_SetDestructor(object, capsule_destructor); + PyCapsule_SetContext(object, capsule_context); + capsule_destructor(object); + CHECK_DESTRUCTOR; + /* intentionally access using the wrong name */ + pointer2 = PyCapsule_GetPointer(object, "the wrong name"); + if (!PyErr_Occurred()) { + FAIL("PyCapsule_GetPointer should have failed but did not!"); + } + PyErr_Clear(); + if (pointer2) { + if (pointer2 == capsule_pointer) { + FAIL("PyCapsule_GetPointer should not have" + " returned the internal pointer!"); + } else { + FAIL("PyCapsule_GetPointer should have " + "returned NULL pointer but did not!"); + } + } + PyCapsule_SetDestructor(object, NULL); + Py_DECREF(object); + if (capsule_destructor_call_count) { + FAIL("destructor called when it should not have been!"); + } + + for (known = &known_capsules[0]; known->module != NULL; known++) { + /* yeah, ordinarily I wouldn't do this either, + but it's fine for this test harness. + */ + static char buffer[256]; +#undef FAIL +#define FAIL(x) \ + { \ + sprintf(buffer, "%s module: \"%s\" attribute: \"%s\"", \ + x, known->module, known->attribute); \ + error = buffer; \ + goto exit; \ + } \ + + PyObject *module = PyImport_ImportModule(known->module); + if (module) { + pointer = PyCapsule_Import(known->name, 0); + if (!pointer) { + Py_DECREF(module); + FAIL("PyCapsule_GetPointer returned NULL unexpectedly!"); + } + object = PyObject_GetAttrString(module, known->attribute); + if (!object) { + Py_DECREF(module); + return NULL; + } + pointer2 = PyCapsule_GetPointer(object, + "weebles wobble but they don't fall down"); + if (!PyErr_Occurred()) { + Py_DECREF(object); + Py_DECREF(module); + FAIL("PyCapsule_GetPointer should have failed but did not!"); + } + PyErr_Clear(); + if (pointer2) { + Py_DECREF(module); + Py_DECREF(object); + if (pointer2 == pointer) { + FAIL("PyCapsule_GetPointer should not have" + " returned its internal pointer!"); + } else { + FAIL("PyCapsule_GetPointer should have" + " returned NULL pointer but did not!"); + } + } + Py_DECREF(object); + Py_DECREF(module); + } + } + + exit: + if (error) { + return raiseTestError("test_capsule", error); + } + Py_RETURN_NONE; +#undef FAIL +} + #ifdef HAVE_GETTIMEOFDAY /* Profiling of integer performance */ static void print_delta(int test, struct timeval *s, struct timeval *e) @@ -1280,9 +1429,10 @@ static PyMethodDef TestMethods[] = { {"test_empty_argparse", (PyCFunction)test_empty_argparse,METH_NOARGS}, {"test_null_strings", (PyCFunction)test_null_strings, METH_NOARGS}, {"test_string_from_format", (PyCFunction)test_string_from_format, METH_NOARGS}, - {"test_string_to_double", (PyCFunction)test_string_to_double, METH_NOARGS}, {"test_with_docstring", (PyCFunction)test_with_docstring, METH_NOARGS, PyDoc_STR("This is a pretty normal docstring.")}, + {"test_string_to_double", (PyCFunction)test_string_to_double, METH_NOARGS}, + {"test_capsule", (PyCFunction)test_capsule, METH_NOARGS}, {"getargs_tuple", getargs_tuple, METH_VARARGS}, {"getargs_keywords", (PyCFunction)getargs_keywords, diff --git a/Modules/cjkcodecs/cjkcodecs.h b/Modules/cjkcodecs/cjkcodecs.h index 0530a33..e630671 100644 --- a/Modules/cjkcodecs/cjkcodecs.h +++ b/Modules/cjkcodecs/cjkcodecs.h @@ -239,6 +239,8 @@ static const struct dbcs_map *mapping_list; static const MultibyteCodec *codec_list = \ (const MultibyteCodec *)_codec_list; + + static PyObject * getmultibytecodec(void) { @@ -284,7 +286,7 @@ getcodec(PyObject *self, PyObject *encoding) return NULL; } - codecobj = PyCObject_FromVoidPtr((void *)codec, NULL); + codecobj = PyCapsule_New((void *)codec, PyMultibyteCodec_CAPSULE_NAME, NULL); if (codecobj == NULL) return NULL; @@ -309,7 +311,7 @@ register_maps(PyObject *module) int r; strcpy(mhname + sizeof("__map_") - 1, h->charset); r = PyModule_AddObject(module, mhname, - PyCObject_FromVoidPtr((void *)h, NULL)); + PyCapsule_New((void *)h, PyMultibyteCodec_CAPSULE_NAME, NULL)); if (r == -1) return -1; } @@ -364,14 +366,14 @@ importmap(const char *modname, const char *symbol, o = PyObject_GetAttrString(mod, (char*)symbol); if (o == NULL) goto errorexit; - else if (!PyCObject_Check(o)) { + else if (!PyCapsule_IsValid(o, PyMultibyteCodec_CAPSULE_NAME)) { PyErr_SetString(PyExc_ValueError, - "map data must be a CObject."); + "map data must be a Capsule."); goto errorexit; } else { struct dbcs_map *map; - map = PyCObject_AsVoidPtr(o); + map = PyCapsule_GetPointer(o, PyMultibyteCodec_CAPSULE_NAME); if (encmap != NULL) *encmap = map->encmap; if (decmap != NULL) diff --git a/Modules/cjkcodecs/multibytecodec.c b/Modules/cjkcodecs/multibytecodec.c index c6b3492..1735dfd 100644 --- a/Modules/cjkcodecs/multibytecodec.c +++ b/Modules/cjkcodecs/multibytecodec.c @@ -1793,12 +1793,12 @@ __create_codec(PyObject *ignore, PyObject *arg) MultibyteCodecObject *self; MultibyteCodec *codec; - if (!PyCObject_Check(arg)) { + if (!PyCapsule_IsValid(arg, PyMultibyteCodec_CAPSULE_NAME)) { PyErr_SetString(PyExc_ValueError, "argument type invalid"); return NULL; } - codec = PyCObject_AsVoidPtr(arg); + codec = PyCapsule_GetPointer(arg, PyMultibyteCodec_CAPSULE_NAME); if (codec->codecinit != NULL && codec->codecinit(codec->config) != 0) return NULL; diff --git a/Modules/cjkcodecs/multibytecodec.h b/Modules/cjkcodecs/multibytecodec.h index 22ea5d4..71c02cc 100644 --- a/Modules/cjkcodecs/multibytecodec.h +++ b/Modules/cjkcodecs/multibytecodec.h @@ -132,6 +132,9 @@ typedef struct { #define MBENC_FLUSH 0x0001 /* encode all characters encodable */ #define MBENC_MAX MBENC_FLUSH +#define PyMultibyteCodec_CAPSULE_NAME "multibytecodec.__map_*" + + #ifdef __cplusplus } #endif diff --git a/Modules/datetimemodule.c b/Modules/datetimemodule.c index 0c6e664..8ba3474 100644 --- a/Modules/datetimemodule.c +++ b/Modules/datetimemodule.c @@ -4792,11 +4792,10 @@ PyInit_datetime(void) Py_INCREF(&PyDateTime_TZInfoType); PyModule_AddObject(m, "tzinfo", (PyObject *) &PyDateTime_TZInfoType); - x = PyCObject_FromVoidPtrAndDesc(&CAPI, (void*) DATETIME_API_MAGIC, - NULL); - if (x == NULL) - return NULL; - PyModule_AddObject(m, "datetime_CAPI", x); + x = PyCapsule_New(&CAPI, PyDateTime_CAPSULE_NAME, NULL); + if (x == NULL) + return NULL; + PyModule_AddObject(m, "datetime_CAPI", x); /* A 4-year cycle has an extra leap day over what we'd get from * pasting together 4 single years. diff --git a/Modules/pyexpat.c b/Modules/pyexpat.c index a968e52..7a862c0 100644 --- a/Modules/pyexpat.c +++ b/Modules/pyexpat.c @@ -1987,8 +1987,8 @@ MODULE_INITFUNC(void) capi.SetUnknownEncodingHandler = XML_SetUnknownEncodingHandler; capi.SetUserData = XML_SetUserData; - /* export as cobject */ - capi_object = PyCObject_FromVoidPtr(&capi, NULL); + /* export using capsule */ + capi_object = PyCapsule_New(&capi, PyExpat_CAPSULE_NAME, NULL); if (capi_object) PyModule_AddObject(m, "expat_CAPI", capi_object); return m; diff --git a/Modules/socketmodule.c b/Modules/socketmodule.c index c229c07..6ec5220 100644 --- a/Modules/socketmodule.c +++ b/Modules/socketmodule.c @@ -4146,6 +4146,14 @@ PySocketModule_APIObject PySocketModuleAPI = NULL }; +PySocketModule_APIObject * +PySocketModule_ImportModuleAndAPI(void) +{ + void *api; + api = PyCapsule_Import(PySocket_CAPSULE_NAME, 1);; + return (PySocketModule_APIObject *)api; +} + /* Initialize the _socket module. @@ -4231,7 +4239,7 @@ PyInit__socket(void) /* Export C API */ if (PyModule_AddObject(m, PySocket_CAPI_NAME, - PyCObject_FromVoidPtr((void *)&PySocketModuleAPI, NULL) + PyCapsule_New(&PySocketModuleAPI, PySocket_CAPSULE_NAME, NULL) ) != 0) return NULL; diff --git a/Modules/socketmodule.h b/Modules/socketmodule.h index 8d19cbf..a323409 100644 --- a/Modules/socketmodule.h +++ b/Modules/socketmodule.h @@ -78,6 +78,7 @@ extern "C" { /* Python module and C API name */ #define PySocket_MODULE_NAME "_socket" #define PySocket_CAPI_NAME "CAPI" +#define PySocket_CAPSULE_NAME PySocket_MODULE_NAME "." PySocket_CAPI_NAME /* Abstract the socket file descriptor type */ #ifdef MS_WINDOWS @@ -142,12 +143,12 @@ typedef struct { the _socket module. Since cross-DLL linking introduces a lot of problems on many platforms, the "trick" is to wrap the C API of a module in a struct which then gets exported to - other modules via a PyCObject. + other modules via a PyCapsule. The code in socketmodule.c defines this struct (which currently only contains the type object reference, but could very well also include other C APIs needed by other modules) - and exports it as PyCObject via the module dictionary + and exports it as PyCapsule via the module dictionary under the name "CAPI". Other modules can now include the socketmodule.h file @@ -212,49 +213,11 @@ typedef struct { ... */ -static -PySocketModule_APIObject PySocketModule; - /* You *must* call this before using any of the functions in PySocketModule and check its outcome; otherwise all accesses will result in a segfault. Returns 0 on success. */ -#ifndef DPRINTF -# define DPRINTF if (0) printf -#endif - -static -int PySocketModule_ImportModuleAndAPI(void) -{ - PyObject *mod = 0, *v = 0; - char *apimodule = PySocket_MODULE_NAME; - char *apiname = PySocket_CAPI_NAME; - void *api; - - DPRINTF("Importing the %s C API...\n", apimodule); - mod = PyImport_ImportModuleNoBlock(apimodule); - if (mod == NULL) - goto onError; - DPRINTF(" %s package found\n", apimodule); - v = PyObject_GetAttrString(mod, apiname); - if (v == NULL) - goto onError; - Py_DECREF(mod); - DPRINTF(" API object %s found\n", apiname); - api = PyCObject_AsVoidPtr(v); - if (api == NULL) - goto onError; - Py_DECREF(v); - memcpy(&PySocketModule, api, sizeof(PySocketModule)); - DPRINTF(" API object loaded and initialized.\n"); - return 0; - - onError: - DPRINTF(" not found.\n"); - Py_XDECREF(mod); - Py_XDECREF(v); - return -1; -} +PyAPI_FUNC(PySocketModule_APIObject *) PySocketModule_ImportModuleAndAPI(void); #endif /* !PySocket_BUILDING_SOCKET */ diff --git a/Modules/unicodedata.c b/Modules/unicodedata.c index 301cee7..2dddc48 100644 --- a/Modules/unicodedata.c +++ b/Modules/unicodedata.c @@ -1275,7 +1275,7 @@ PyInit_unicodedata(void) PyModule_AddObject(m, "ucd_3_2_0", v); /* Export C API */ - v = PyCObject_FromVoidPtr((void *) &hashAPI, NULL); + v = PyCapsule_New((void *)&hashAPI, PyUnicodeData_CAPSULE_NAME, NULL); if (v != NULL) PyModule_AddObject(m, "ucnhash_CAPI", v); return m; diff --git a/Objects/capsule.c b/Objects/capsule.c new file mode 100644 index 0000000..5304ce1 --- /dev/null +++ b/Objects/capsule.c @@ -0,0 +1,324 @@ +/* Wrap void * pointers to be passed between C modules */ + +#include "Python.h" + +/* Internal structure of PyCapsule */ +typedef struct { + PyObject_HEAD + void *pointer; + const char *name; + void *context; + PyCapsule_Destructor destructor; +} PyCapsule; + + + +static int +_is_legal_capsule(PyCapsule *capsule, const char *invalid_capsule) +{ + if (!capsule || !PyCapsule_CheckExact(capsule) || capsule->pointer == NULL) { + PyErr_SetString(PyExc_ValueError, invalid_capsule); + return 0; + } + return 1; +} + +#define is_legal_capsule(capsule, name) \ + (_is_legal_capsule(capsule, \ + name " called with invalid PyCapsule object")) + + +static int +name_matches(const char *name1, const char *name2) { + /* if either is NULL, */ + if (!name1 || !name2) { + /* they're only the same if they're both NULL. */ + return name2 == name2; + } + return !strcmp(name1, name2); +} + + + +PyObject * +PyCapsule_New(void *pointer, const char *name, PyCapsule_Destructor destructor) +{ + PyCapsule *capsule; + + if (!pointer) { + PyErr_SetString(PyExc_ValueError, "PyCapsule_New called with null pointer"); + return NULL; + } + + capsule = PyObject_NEW(PyCapsule, &PyCapsule_Type); + if (capsule == NULL) { + return NULL; + } + + capsule->pointer = pointer; + capsule->name = name; + capsule->context = NULL; + capsule->destructor = destructor; + + return (PyObject *)capsule; +} + + +int +PyCapsule_IsValid(PyObject *o, const char *name) +{ + PyCapsule *capsule = (PyCapsule *)o; + + return (capsule != NULL && + PyCapsule_CheckExact(capsule) && + capsule->pointer != NULL && + name_matches(capsule->name, name)); +} + + +void * +PyCapsule_GetPointer(PyObject *o, const char *name) +{ + PyCapsule *capsule = (PyCapsule *)o; + + if (!is_legal_capsule(capsule, "PyCapsule_GetPointer")) { + return NULL; + } + + if (!name_matches(name, capsule->name)) { + PyErr_SetString(PyExc_ValueError, "PyCapsule_GetPointer called with incorrect name"); + return NULL; + } + + return capsule->pointer; +} + + +const char * +PyCapsule_GetName(PyObject *o) +{ + PyCapsule *capsule = (PyCapsule *)o; + + if (!is_legal_capsule(capsule, "PyCapsule_GetName")) { + return NULL; + } + return capsule->name; +} + + +PyCapsule_Destructor +PyCapsule_GetDestructor(PyObject *o) +{ + PyCapsule *capsule = (PyCapsule *)o; + + if (!is_legal_capsule(capsule, "PyCapsule_GetDestructor")) { + return NULL; + } + return capsule->destructor; +} + + +void * +PyCapsule_GetContext(PyObject *o) +{ + PyCapsule *capsule = (PyCapsule *)o; + + if (!is_legal_capsule(capsule, "PyCapsule_GetContext")) { + return NULL; + } + return capsule->context; +} + + +int +PyCapsule_SetPointer(PyObject *o, void *pointer) +{ + PyCapsule *capsule = (PyCapsule *)o; + + if (!pointer) { + PyErr_SetString(PyExc_ValueError, "PyCapsule_SetPointer called with null pointer"); + return -1; + } + + if (!is_legal_capsule(capsule, "PyCapsule_SetPointer")) { + return -1; + } + + capsule->pointer = pointer; + return 0; +} + + +int +PyCapsule_SetName(PyObject *o, const char *name) +{ + PyCapsule *capsule = (PyCapsule *)o; + + if (!is_legal_capsule(capsule, "PyCapsule_SetName")) { + return -1; + } + + capsule->name = name; + return 0; +} + + +int +PyCapsule_SetDestructor(PyObject *o, PyCapsule_Destructor destructor) +{ + PyCapsule *capsule = (PyCapsule *)o; + + if (!is_legal_capsule(capsule, "PyCapsule_SetDestructor")) { + return -1; + } + + capsule->destructor = destructor; + return 0; +} + + +int +PyCapsule_SetContext(PyObject *o, void *context) +{ + PyCapsule *capsule = (PyCapsule *)o; + + if (!is_legal_capsule(capsule, "PyCapsule_SetContext")) { + return -1; + } + + capsule->context = context; + return 0; +} + + +void * +PyCapsule_Import(const char *name, int no_block) +{ + PyObject *object = NULL; + void *return_value = NULL; + char *trace; + int name_length = (strlen(name) + 1) * sizeof(char); + char *name_dup = (char *)PyMem_MALLOC(name_length); + + if (!name_dup) { + return NULL; + } + + memcpy(name_dup, name, name_length); + + trace = name_dup; + while (trace) { + char *dot = strchr(trace, '.'); + if (dot) { + *dot++ = '\0'; + } + + if (object == NULL) { + if (no_block) { + object = PyImport_ImportModuleNoBlock(trace); + } else { + object = PyImport_ImportModule(trace); + if (!object) { + PyErr_Format(PyExc_ImportError, "PyCapsule_Import could not import module \"%s\"", trace); + } + } + } else { + PyObject *object2 = PyObject_GetAttrString(object, trace); + Py_DECREF(object); + object = object2; + } + if (!object) { + goto EXIT; + } + + trace = dot; + } + + /* compare attribute name to module.name by hand */ + if (PyCapsule_IsValid(object, name)) { + PyCapsule *capsule = (PyCapsule *)object; + return_value = capsule->pointer; + } else { + PyErr_Format(PyExc_AttributeError, + "PyCapsule_Import \"%s\" is not valid", + name); + } + +EXIT: + Py_XDECREF(object); + if (name_dup) { + PyMem_FREE(name_dup); + } + return return_value; +} + + +static void +capsule_dealloc(PyObject *o) +{ + PyCapsule *capsule = (PyCapsule *)o; + if (capsule->destructor) { + capsule->destructor(o); + } + PyObject_DEL(o); +} + + +static PyObject * +capsule_repr(PyObject *o) +{ + PyCapsule *capsule = (PyCapsule *)o; + const char *name; + const char *quote; + + if (capsule->name) { + quote = "\""; + name = capsule->name; + } else { + quote = ""; + name = "NULL"; + } + + return PyUnicode_FromFormat("", + quote, name, quote, capsule); +} + + + +PyDoc_STRVAR(PyCapsule_Type__doc__, +"Capsule objects let you wrap a C \"void *\" pointer in a Python\n\ +object. They're a way of passing data through the Python interpreter\n\ +without creating your own custom type.\n\ +\n\ +Capsules are used for communication between extension modules.\n\ +They provide a way for an extension module to export a C interface\n\ +to other extension modules, so that extension modules can use the\n\ +Python import mechanism to link to one another.\n\ +"); + +PyTypeObject PyCapsule_Type = { + PyVarObject_HEAD_INIT(&PyType_Type, 0) + "PyCapsule", /*tp_name*/ + sizeof(PyCapsule), /*tp_basicsize*/ + 0, /*tp_itemsize*/ + /* methods */ + capsule_dealloc, /*tp_dealloc*/ + 0, /*tp_print*/ + 0, /*tp_getattr*/ + 0, /*tp_setattr*/ + 0, /*tp_reserved*/ + capsule_repr, /*tp_repr*/ + 0, /*tp_as_number*/ + 0, /*tp_as_sequence*/ + 0, /*tp_as_mapping*/ + 0, /*tp_hash*/ + 0, /*tp_call*/ + 0, /*tp_str*/ + 0, /*tp_getattro*/ + 0, /*tp_setattro*/ + 0, /*tp_as_buffer*/ + 0, /*tp_flags*/ + PyCapsule_Type__doc__ /*tp_doc*/ +}; + + diff --git a/Objects/object.c b/Objects/object.c index bf347b9..57b4906 100644 --- a/Objects/object.c +++ b/Objects/object.c @@ -1716,11 +1716,14 @@ _Py_GetObjects(PyObject *self, PyObject *args) #endif - /* Hack to force loading of cobject.o */ PyTypeObject *_Py_cobject_hack = &PyCObject_Type; +/* Hack to force loading of pycapsule.o */ +PyTypeObject *_PyCapsule_hack = &PyCapsule_Type; + + /* Hack to force loading of abstract.o */ Py_ssize_t (*_Py_abstract_hack)(PyObject *) = PyObject_Size; diff --git a/Objects/unicodeobject.c b/Objects/unicodeobject.c index fc5c1c5..3740892 100644 --- a/Objects/unicodeobject.c +++ b/Objects/unicodeobject.c @@ -3381,16 +3381,7 @@ PyObject *PyUnicode_DecodeUnicodeEscape(const char *s, message = "malformed \\N character escape"; if (ucnhash_CAPI == NULL) { /* load the unicode data module */ - PyObject *m, *api; - m = PyImport_ImportModuleNoBlock("unicodedata"); - if (m == NULL) - goto ucnhashError; - api = PyObject_GetAttrString(m, "ucnhash_CAPI"); - Py_DECREF(m); - if (api == NULL) - goto ucnhashError; - ucnhash_CAPI = (_PyUnicode_Name_CAPI *)PyCObject_AsVoidPtr(api); - Py_DECREF(api); + ucnhash_CAPI = (_PyUnicode_Name_CAPI *)PyCapsule_Import(PyUnicodeData_CAPSULE_NAME, 1); if (ucnhash_CAPI == NULL) goto ucnhashError; } diff --git a/PC/os2emx/python27.def b/PC/os2emx/python27.def index 3cd1ad3..0776ced 100644 --- a/PC/os2emx/python27.def +++ b/PC/os2emx/python27.def @@ -209,6 +209,19 @@ EXPORTS "PyInstance_Type" "PyMethod_Type" +; From python26_s.lib(capsule) + "PyCapsule_GetContext" + "PyCapsule_GetDestructor" + "PyCapsule_GetName" + "PyCapsule_GetPointer" + "PyCapsule_Import" + "PyCapsule_IsValid" + "PyCapsule_New" + "PyCapsule_SetContext" + "PyCapsule_SetDestructor" + "PyCapsule_SetName" + "PyCapsule_SetPointer" + ; From python26_s.lib(cobject) "PyCObject_FromVoidPtr" "PyCObject_FromVoidPtrAndDesc" diff --git a/PC/os2vacpp/python.def b/PC/os2vacpp/python.def index 94e094e..7d26546 100644 --- a/PC/os2vacpp/python.def +++ b/PC/os2vacpp/python.def @@ -6,6 +6,7 @@ DATA MULTIPLE NONSHARED EXPORTS ; Data PyCFunction_Type + PyCapsule_Type PyCObject_Type PyClass_Type PyCode_Type @@ -73,7 +74,7 @@ EXPORTS _Py_TrueStruct _Py_ZeroStruct _Py_abstract_hack - _Py_cobject_hack + _Py_capsule_hack _Py_re_syntax _Py_re_syntax_table @@ -87,6 +88,17 @@ EXPORTS PyCFunction_GetFunction PyCFunction_GetSelf PyCFunction_New + PyCapsule_GetContext + PyCapsule_GetDestructor + PyCapsule_GetName + PyCapsule_GetPointer + PyCapsule_Import + PyCapsule_IsValid + PyCapsule_New + PyCapsule_SetContext + PyCapsule_SetDestructor + PyCapsule_SetName + PyCapsule_SetPointer PyCObject_AsVoidPtr PyCObject_FromVoidPtrAndDesc PyCObject_FromVoidPtr diff --git a/PCbuild/pythoncore.vcproj b/PCbuild/pythoncore.vcproj index bc0abfc..05cffd7 100644 --- a/PCbuild/pythoncore.vcproj +++ b/PCbuild/pythoncore.vcproj @@ -846,6 +846,7 @@ RelativePath="..\Include\pyarena.h" > + @@ -1374,6 +1375,10 @@ RelativePath="..\Objects\bytesobject.c" > + + diff --git a/Python/compile.c b/Python/compile.c index 95ebd76..c78949d 100644 --- a/Python/compile.c +++ b/Python/compile.c @@ -190,6 +190,8 @@ static int compiler_call_helper(struct compiler *c, int n, static PyCodeObject *assemble(struct compiler *, int addNone); static PyObject *__doc__; +#define COMPILER_CAPSULE_NAME_COMPILER_UNIT "compile.c compiler unit" + PyObject * _Py_Mangle(PyObject *privateobj, PyObject *ident) { @@ -506,13 +508,13 @@ compiler_enter_scope(struct compiler *c, identifier name, void *key, /* Push the old compiler_unit on the stack. */ if (c->u) { - PyObject *wrapper = PyCObject_FromVoidPtr(c->u, NULL); - if (!wrapper || PyList_Append(c->c_stack, wrapper) < 0) { - Py_XDECREF(wrapper); + PyObject *capsule = PyCapsule_New(c->u, COMPILER_CAPSULE_NAME_COMPILER_UNIT, NULL); + if (!capsule || PyList_Append(c->c_stack, capsule) < 0) { + Py_XDECREF(capsule); compiler_unit_free(u); return 0; } - Py_DECREF(wrapper); + Py_DECREF(capsule); u->u_private = c->u->u_private; Py_XINCREF(u->u_private); } @@ -529,15 +531,15 @@ static void compiler_exit_scope(struct compiler *c) { int n; - PyObject *wrapper; + PyObject *capsule; c->c_nestlevel--; compiler_unit_free(c->u); /* Restore c->u to the parent unit. */ n = PyList_GET_SIZE(c->c_stack) - 1; if (n >= 0) { - wrapper = PyList_GET_ITEM(c->c_stack, n); - c->u = (struct compiler_unit *)PyCObject_AsVoidPtr(wrapper); + capsule = PyList_GET_ITEM(c->c_stack, n); + c->u = (struct compiler_unit *)PyCapsule_GetPointer(capsule, COMPILER_CAPSULE_NAME_COMPILER_UNIT); assert(c->u); /* we are deleting from a list so this really shouldn't fail */ if (PySequence_DelItem(c->c_stack, n) < 0) diff --git a/Python/getargs.c b/Python/getargs.c index 4b0ea84..15f6dd2 100644 --- a/Python/getargs.c +++ b/Python/getargs.c @@ -139,22 +139,33 @@ _PyArg_VaParse_SizeT(PyObject *args, char *format, va_list va) /* Handle cleanup of allocated memory in case of exception */ +#define GETARGS_CAPSULE_NAME_CLEANUP_PTR "getargs.cleanup_ptr" +#define GETARGS_CAPSULE_NAME_CLEANUP_BUFFER "getargs.cleanup_buffer" + static void -cleanup_ptr(void *ptr) +cleanup_ptr(PyObject *self) { - PyMem_FREE(ptr); + void *ptr = PyCapsule_GetPointer(self, GETARGS_CAPSULE_NAME_CLEANUP_PTR); + if (ptr) { + PyMem_FREE(ptr); + } } static void -cleanup_buffer(void *ptr) +cleanup_buffer(PyObject *self) { - PyBuffer_Release((Py_buffer *) ptr); + Py_buffer *ptr = (Py_buffer *)PyCapsule_GetPointer(self, GETARGS_CAPSULE_NAME_CLEANUP_BUFFER); + if (ptr) { + PyBuffer_Release(ptr); + } } static int -addcleanup(void *ptr, PyObject **freelist, void (*destr)(void *)) +addcleanup(void *ptr, PyObject **freelist, PyCapsule_Destructor destr) { PyObject *cobj; + const char *name; + if (!*freelist) { *freelist = PyList_New(0); if (!*freelist) { @@ -162,7 +173,15 @@ addcleanup(void *ptr, PyObject **freelist, void (*destr)(void *)) return -1; } } - cobj = PyCObject_FromVoidPtr(ptr, destr); + + if (destr == cleanup_ptr) { + name = GETARGS_CAPSULE_NAME_CLEANUP_PTR; + } else if (destr == cleanup_buffer) { + name = GETARGS_CAPSULE_NAME_CLEANUP_BUFFER; + } else { + return -1; + } + cobj = PyCapsule_New(ptr, name, destr); if (!cobj) { destr(ptr); return -1; @@ -183,8 +202,7 @@ cleanreturn(int retval, PyObject *freelist) don't get called. */ Py_ssize_t len = PyList_GET_SIZE(freelist), i; for (i = 0; i < len; i++) - ((PyCObject *) PyList_GET_ITEM(freelist, i)) - ->destructor = NULL; + PyCapsule_SetDestructor(PyList_GET_ITEM(freelist, i), NULL); } Py_XDECREF(freelist); return retval; -- cgit v0.12