From 402b73fb8d54ec2b24b52fdd77d389d903fa6c44 Mon Sep 17 00:00:00 2001 From: Larry Hastings Date: Thu, 25 Mar 2010 00:54:54 +0000 Subject: Backported PyCapsule from 3.1, and converted most uses of CObject to PyCapsule. --- Doc/c-api/capsule.rst | 150 +++++++++++++++++ Doc/c-api/cobject.rst | 5 +- Doc/c-api/concrete.rst | 1 + Doc/data/refcounts.dat | 39 +++++ Doc/extending/extending.rst | 69 ++++---- Include/Python.h | 1 + Include/cStringIO.h | 7 +- Include/cobject.h | 2 + Include/datetime.h | 12 +- Include/py_curses.h | 15 +- Include/pycapsule.h | 148 +++++++++++++++++ Include/pyexpat.h | 1 + Include/ucnhash.h | 4 +- Lib/test/test_sys.py | 1 + Makefile.pre.in | 2 + Misc/NEWS | 6 + Modules/_ctypes/callproc.c | 23 ++- Modules/_ctypes/cfield.c | 7 +- Modules/_ctypes/ctypes.h | 34 ++++ Modules/_cursesmodule.c | 6 +- Modules/_elementtree.c | 2 +- Modules/_testcapimodule.c | 152 +++++++++++++++++ Modules/cStringIO.c | 4 +- Modules/cjkcodecs/cjkcodecs.h | 10 +- Modules/cjkcodecs/multibytecodec.c | 4 +- Modules/cjkcodecs/multibytecodec.h | 3 + Modules/datetimemodule.c | 9 +- Modules/pyexpat.c | 4 +- Modules/socketmodule.c | 2 +- Modules/socketmodule.h | 24 +-- Modules/unicodedata.c | 2 +- Objects/capsule.c | 324 +++++++++++++++++++++++++++++++++++++ Objects/object.c | 4 + Objects/unicodeobject.c | 11 +- PC/VS7.1/pythoncore.vcproj | 3 + PC/VS8.0/pythoncore.vcproj | 8 + PC/os2emx/python27.def | 13 ++ PC/os2vacpp/python.def | 13 ++ Python/compile.c | 16 +- Python/getargs.c | 34 +++- 40 files changed, 1048 insertions(+), 127 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..2939314 --- /dev/null +++ b/Doc/c-api/capsule.rst @@ -0,0 +1,150 @@ +.. 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*. + + On failure, set an exception and return *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 as its argument when it is destroyed. + + If this capsule will be stored as an attribute of a module, the *name* should + be specified as ``modulename.attributename``. This will enable other modules + to import the capsule using :cfunc:`PyCapsule_Import`. + + +.. cfunction:: void* PyCapsule_GetPointer(PyObject *capsule, const char *name) + + Retrieve the *pointer* stored in the capsule. On failure, set an exception + and return *NULL*. + + 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*. Python uses the C function :cfunc:`strcmp` to compare capsule + names. + + +.. 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 disambiguate. + + +.. 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 disambiguate. + + +.. 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 disambiguate. + + +.. 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*. However, if :cfunc:`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 *capsule* is a valid capsule. A valid capsule is + non-*NULL*, passes :cfunc:`PyCapsule_CheckExact`, has a non-*NULL* pointer + stored in it, 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, PyCapsule_Destructor 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..73fbbf5 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 2.7. 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 0595788..5ee611b 100644 --- a/Doc/c-api/concrete.rst +++ b/Doc/c-api/concrete.rst @@ -100,6 +100,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 b902bde..16f0604 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 c7bff3e..5408b88 100644 --- a/Doc/extending/extending.rst +++ b/Doc/extending/extending.rst @@ -1060,7 +1060,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 ========================================= @@ -1096,23 +1096,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. @@ -1174,8 +1191,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); @@ -1217,28 +1234,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 *c_api_object; - PyObject *module; - - module = PyImport_ImportModule("spam"); - if (module == NULL) - return -1; - - c_api_object = PyObject_GetAttrString(module, "_C_API"); - if (c_api_object == NULL) { - Py_DECREF(module); - return -1; - } - if (PyCObject_Check(c_api_object)) - PySpam_API = (void **)PyCObject_AsVoidPtr(c_api_object); - - Py_DECREF(c_api_object); - Py_DECREF(module); - return 0; + PySpam_API = (void **)PyCapsule_Import("spam._C_API", 0); + return (PySpam_API != NULL) ? 0 : -1; } #endif @@ -1270,11 +1273,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 eac9f48..75d2b10 100644 --- a/Include/Python.h +++ b/Include/Python.h @@ -107,6 +107,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/cStringIO.h b/Include/cStringIO.h index d22b9eb..6ca44a8 100644 --- a/Include/cStringIO.h +++ b/Include/cStringIO.h @@ -18,9 +18,12 @@ extern "C" { This would typically be done in your init function. */ + +#define PycStringIO_CAPSULE_NAME "cStringIO.cStringIO_CAPI" + #define PycString_IMPORT \ - PycStringIO = (struct PycStringIO_CAPI*)PyCObject_Import("cStringIO", \ - "cStringIO_CAPI") + PycStringIO = ((struct PycStringIO_CAPI*)PyCapsule_Import(\ + PycStringIO_CAPSULE_NAME, 0)) /* Basic functions to manipulate cStringIO objects from C */ diff --git a/Include/cobject.h b/Include/cobject.h index 4e24d7d..e5e246b 100644 --- a/Include/cobject.h +++ b/Include/cobject.h @@ -6,6 +6,8 @@ to other extension modules, so that extension modules can use the Python import mechanism to link to one another. + DEPRECATED - Use PyCapsule objects instead. + CObject will be removed in 2.8 (if there is one). */ #ifndef Py_COBJECT_H diff --git a/Include/datetime.h b/Include/datetime.h index 490d5ca..e017299 100644 --- a/Include/datetime.h +++ b/Include/datetime.h @@ -158,6 +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 @@ -186,15 +188,7 @@ typedef struct { static PyDateTime_CAPI *PyDateTimeAPI = NULL; #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 e4c0a6e..657816c 100644 --- a/Include/py_curses.h +++ b/Include/py_curses.h @@ -80,6 +80,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 */ @@ -94,16 +97,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..2cc1c73 --- /dev/null +++ b/Include/pycapsule.h @@ -0,0 +1,148 @@ + +/* 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); + + +#define PYTHON_USING_CAPSULE + +#define PYCAPSULE_INSTANTIATE_DESTRUCTOR(name, destructor) \ +static void pycapsule_destructor_ ## name(PyObject *ptr) \ +{ \ + void *p = PyCapsule_GetPointer(ptr, name); \ + if (p) { \ + destructor(p); \ + } \ +} \ + +#define PYCAPSULE_NEW(pointer, name) \ + (PyCapsule_New(pointer, name, capsule_destructor_ ## name)) + +#define PYCAPSULE_ISVALID(capsule, name) \ + (PyCapsule_IsValid(capsule, name)) + +#define PYCAPSULE_DEREFERENCE(capsule, name) \ + (PyCapsule_GetPointer(capsule, name)) + +#define PYCAPSULE_SET(capsule, name, value) \ + (PyCapsule_IsValid(capsule, name) && PyCapsule_SetPointer(capsule, value)) + +/* module and attribute should be specified as string constants */ +#define PYCAPSULE_IMPORT(module, attribute) \ + (PyCapsule_Import(module "." attribute, 0)) + + +/* begin public-domain code */ +/* +** This code was written by Larry Hastings, +** and is dedicated to the public domain. +** It's designed to make it easy to switch +** from CObject to Capsule objects without losing +** backwards compatibility with prior versions +** of CPython. You're encouraged to copy this code +** (including this documentation) into your +** Python C extension. +** +** To use: +** * #define a name for the pointer you store in +** the CObject. If you make the CObject available +** as part of your module's API, this name should +** be "modulename.attributename", and it should be +** considered part of your API (so put it in your +** header file). +** * Specify a PYCAPSULE_INSTANTIATE_DESTRUCTOR(), in +** every C file that creates these CObjects. This +** is where you specify your object's destructor. +** * Change all calls to CObject_FromVoidPtr() +** and CObject_FromVoidPointerAndDesc() into +** PYCAPSULE_NEW() calls. +** * Change all calls to PyCObject_AsVoidPtr() +** into PYCAPSULE_DEREFERENCE() calls. +** * Change all calls to PyCObject_SetVoidPtr() +** into PYCAPSULE_SET() calls. +** * Change all calls to PyCObject_Import() +** into PYCAPSULE_IMPORT() calls. Note that +** the two arguments to PYCAPSULE_IMPORT() +** should both be string constants; that is, +** you should call +** PYCAPSULE_IMPORT("modulename", "attributename"), +** not PYCAPSULE_IMPORT(charstar1, charstar2). +*/ +#ifndef PYTHON_USING_CAPSULE + +#define PYCAPSULE_INSTANTIATE_DESTRUCTOR(name, destructor) \ +static void pycapsule_destructor_ ## name(void *ptr) \ +{ \ + destructor(p); \ +} \ + +#define PYCAPSULE_NEW(pointer, name) \ + (PyCObject_FromVoidPtr(pointer, pycapsule_destructor_ ## name)) + +#define PYCAPSULE_ISVALID(capsule, name) \ + (PyCObject_Check(capsule)) + +#define PYCAPSULE_DEREFERENCE(capsule, name) \ + (PyCObject_AsVoidPtr(capsule)) + +#define PYCAPSULE_SET(capsule, name, value) \ + (PyCObject_SetVoidPtr(capsule, value)) + +/* module and attribute should be specified as string constants */ +#define PYCAPSULE_IMPORT(module, attribute) \ + (PyCObject_Import(module, attribute)) + +#endif /* PYTHON_USING_CAPSULE */ +/* end public-domain code */ + +#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 58a860c..d106fe7 100644 --- a/Lib/test/test_sys.py +++ b/Lib/test/test_sys.py @@ -670,6 +670,7 @@ class SizeofTest(unittest.TestCase): x = property(getx, setx, delx, "") check(x, size(h + '4Pi')) # PyCObject + # PyCapsule # XXX # rangeiterator check(iter(xrange(1)), size(h + '4l')) diff --git a/Makefile.pre.in b/Makefile.pre.in index 6b8b739..0f0e246 100644 --- a/Makefile.pre.in +++ b/Makefile.pre.in @@ -309,6 +309,7 @@ OBJECT_OBJS= \ Objects/bufferobject.o \ Objects/bytes_methods.o \ Objects/bytearrayobject.o \ + Objects/capsule.o \ Objects/cellobject.o \ Objects/classobject.o \ Objects/cobject.o \ @@ -642,6 +643,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 bebf2b7..d834e10 100644 --- a/Misc/NEWS +++ b/Misc/NEWS @@ -138,6 +138,12 @@ Build - Issue #7705: Fix linking on FreeBSD. +C-API +----- + +- Issue #7992: A replacement PyCObject API, PyCapsule, has been backported + from Python 3.1. + Tests ----- diff --git a/Modules/_ctypes/callproc.c b/Modules/_ctypes/callproc.c index cf17e3a..8e0b6a0 100644 --- a/Modules/_ctypes/callproc.c +++ b/Modules/_ctypes/callproc.c @@ -83,6 +83,13 @@ #define DONT_USE_SEH #endif + +#define CTYPES_CAPSULE_ERROROBJ "_ctypes/callproc.c error object" +CTYPES_CAPSULE_INSTANTIATE_DESTRUCTOR(CTYPES_CAPSULE_ERROROBJ) + +#define CTYPES_CAPSULE_WCHAR_T "_ctypes/callproc.c wchar_t buffer from unicode" +CTYPES_CAPSULE_INSTANTIATE_DESTRUCTOR(CTYPES_CAPSULE_WCHAR_T) + /* 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 @@ -134,14 +141,22 @@ _ctypes_get_errobj(int **pspace) return NULL; } errobj = PyDict_GetItem(dict, error_object_name); - if (errobj) + if (errobj) { +#ifdef CTYPES_USING_CAPSULE + if (!PyCapsule_IsValid(errobj, CTYPES_CAPSULE_ERROROBJ)) { + PyErr_SetString(PyExc_RuntimeError, + "ctypes.error_object is an invalid capsule"); + return NULL; + } +#endif /* CTYPES_USING_CAPSULE */ Py_INCREF(errobj); + } else { void *space = PyMem_Malloc(sizeof(int) * 2); if (space == NULL) return NULL; memset(space, 0, sizeof(int) * 2); - errobj = PyCObject_FromVoidPtr(space, PyMem_Free); + errobj = CAPSULE_NEW(space, CTYPES_CAPSULE_ERROROBJ); if (errobj == NULL) return NULL; if (-1 == PyDict_SetItem(dict, error_object_name, @@ -150,7 +165,7 @@ _ctypes_get_errobj(int **pspace) return NULL; } } - *pspace = (int *)PyCObject_AsVoidPtr(errobj); + *pspace = (int *)CAPSULE_DEREFERENCE(errobj, CTYPES_CAPSULE_ERROROBJ); return errobj; } @@ -670,7 +685,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 = CAPSULE_NEW(pa->value.p, CTYPES_CAPSULE_WCHAR_T); if (!pa->keep) { PyMem_Free(pa->value.p); return -1; diff --git a/Modules/_ctypes/cfield.c b/Modules/_ctypes/cfield.c index bf247bc..d7fc5d2 100644 --- a/Modules/_ctypes/cfield.c +++ b/Modules/_ctypes/cfield.c @@ -10,6 +10,11 @@ #endif #include "ctypes.h" + +#define CTYPES_CAPSULE_WCHAR_T "_ctypes/cfield.c wchar_t buffer from unicode" +CTYPES_CAPSULE_INSTANTIATE_DESTRUCTOR(CTYPES_CAPSULE_WCHAR_T) + + /******************************************************************/ /* PyCField_Type @@ -1457,7 +1462,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 = CAPSULE_NEW(buffer, CTYPES_CAPSULE_WCHAR_T); if (!keep) { Py_DECREF(value); PyMem_Free(buffer); diff --git a/Modules/_ctypes/ctypes.h b/Modules/_ctypes/ctypes.h index 1003614..718ee52 100644 --- a/Modules/_ctypes/ctypes.h +++ b/Modules/_ctypes/ctypes.h @@ -445,6 +445,40 @@ PyObject *_ctypes_get_errobj(int **pspace); extern PyObject *ComError; #endif +#if PY_VERSION_HEX >= 0x020700A4 +/* Use PyCapsule for 2.7 */ + +#define CTYPES_USING_CAPSULE + +#define CTYPES_CAPSULE_INSTANTIATE_DESTRUCTOR(name) \ +static void capsule_destructor_ ## name(PyObject *ptr) \ +{ \ + void *p = PyCapsule_GetPointer(ptr, name); \ + if (p) { \ + PyMem_Free(p); \ + } \ +} \ + +#define CAPSULE_NEW(pointer, name) \ + (PyCapsule_New(pointer, name, capsule_destructor_ ## name)) + +#define CAPSULE_DEREFERENCE(capsule, name) \ + (PyCapsule_GetPointer(capsule, name)) + +#else /* PY_VERSION_HEX >= 0x020700A4 */ +/* Use CObject for 2.6 and before */ + +#define CTYPES_CAPSULE_INSTANTIATE_DESTRUCTOR(name) + +#define CAPSULE_NEW(pointer, name) \ + (PyCObject_FromVoidPtr(pointer, PyMem_Free)) + +#define CAPSULE_DEREFERENCE(capsule, name) \ + (PyCObject_AsVoidPtr(capsule)) + +#endif /* PY_VERSION_HEX >= 0x020700A4 */ + + /* Local Variables: compile-command: "python setup.py -q build install --home ~" diff --git a/Modules/_cursesmodule.c b/Modules/_cursesmodule.c index 430ab17..3f95cef 100644 --- a/Modules/_cursesmodule.c +++ b/Modules/_cursesmodule.c @@ -172,7 +172,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 * @@ -2745,8 +2745,8 @@ init_curses(void) return; 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 0aa1ebb..4311ba2 100644 --- a/Modules/_elementtree.c +++ b/Modules/_elementtree.c @@ -3059,7 +3059,7 @@ init_elementtree(void) #if defined(USE_PYEXPAT_CAPI) /* link against pyexpat, if possible */ - expat_capi = PyCObject_Import("pyexpat", "expat_CAPI"); + expat_capi = PyCapsule_Import(PyExpat_CAPSULE_NAME, 0); if (expat_capi) { /* check that it's usable */ if (strcmp(expat_capi->magic, PyExpat_CAPI_MAGIC) != 0 || diff --git a/Modules/_testcapimodule.c b/Modules/_testcapimodule.c index 6bc6da2..023e62e 100644 --- a/Modules/_testcapimodule.c +++ b/Modules/_testcapimodule.c @@ -1428,6 +1428,157 @@ test_string_from_format(PyObject *self, PyObject *args) #undef CHECK_1_FORMAT } +/* 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); + } + else + PyErr_Clear(); + } + + exit: + if (error) { + return raiseTestError("test_capsule", error); + } + Py_RETURN_NONE; +#undef FAIL +} + /* This is here to provide a docstring for test_descr. */ static PyObject * test_with_docstring(PyObject *self) @@ -1539,6 +1690,7 @@ static PyMethodDef TestMethods[] = { {"_test_thread_state", test_thread_state, METH_VARARGS}, {"_pending_threadfunc", pending_threadfunc, METH_VARARGS}, #endif + {"test_capsule", (PyCFunction)test_capsule, METH_NOARGS}, {"traceback_print", traceback_print, METH_VARARGS}, {"code_newempty", code_newempty, METH_VARARGS}, {"make_exception_with_doc", (PyCFunction)make_exception_with_doc, diff --git a/Modules/cStringIO.c b/Modules/cStringIO.c index 237d8c2..80588bf 100644 --- a/Modules/cStringIO.c +++ b/Modules/cStringIO.c @@ -761,8 +761,8 @@ initcStringIO(void) { Py_TYPE(&Otype)=&PyType_Type; if (PyType_Ready(&Otype) < 0) return; if (PyType_Ready(&Itype) < 0) return; - PyDict_SetItemString(d,"cStringIO_CAPI", - v = PyCObject_FromVoidPtr(&CAPI,NULL)); + v = PyCapsule_New(&CAPI, PycStringIO_CAPSULE_NAME, NULL); + PyDict_SetItemString(d,"cStringIO_CAPI", v); Py_XDECREF(v); /* Export Types */ diff --git a/Modules/cjkcodecs/cjkcodecs.h b/Modules/cjkcodecs/cjkcodecs.h index 4005bcf..236c3fe 100644 --- a/Modules/cjkcodecs/cjkcodecs.h +++ b/Modules/cjkcodecs/cjkcodecs.h @@ -282,7 +282,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; @@ -307,7 +307,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; } @@ -362,14 +362,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 5fb5ec0..794774b 100644 --- a/Modules/cjkcodecs/multibytecodec.c +++ b/Modules/cjkcodecs/multibytecodec.c @@ -1774,12 +1774,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 7b9e271..3f1eb57 100644 --- a/Modules/datetimemodule.c +++ b/Modules/datetimemodule.c @@ -4856,11 +4856,10 @@ initdatetime(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; - 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 47ef186..d428c66 100644 --- a/Modules/pyexpat.c +++ b/Modules/pyexpat.c @@ -2053,8 +2053,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); } diff --git a/Modules/socketmodule.c b/Modules/socketmodule.c index fbd0239..d44a691 100644 --- a/Modules/socketmodule.c +++ b/Modules/socketmodule.c @@ -4496,7 +4496,7 @@ init_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; diff --git a/Modules/socketmodule.h b/Modules/socketmodule.h index ef8d0fc..48f230e 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 @@ -226,33 +227,18 @@ PySocketModule_APIObject PySocketModule; 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); + DPRINTF(" Loading capsule %s\n", PySocket_CAPSULE_NAME); + api = PyCapsule_Import(PySocket_CAPSULE_NAME, 1); 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; } diff --git a/Modules/unicodedata.c b/Modules/unicodedata.c index 552dae5..ede57cb 100644 --- a/Modules/unicodedata.c +++ b/Modules/unicodedata.c @@ -1261,7 +1261,7 @@ initunicodedata(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); } diff --git a/Objects/capsule.c b/Objects/capsule.c new file mode 100644 index 0000000..ea20f82 --- /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 name1 == 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; + size_t 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 PyString_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 3c7facb..60dc188 100644 --- a/Objects/object.c +++ b/Objects/object.c @@ -2269,6 +2269,10 @@ _Py_GetObjects(PyObject *self, PyObject *args) #endif +/* Hack to force loading of capsule.o */ +PyTypeObject *_Py_capsule_hack = &PyCapsule_Type; + + /* Hack to force loading of cobject.o */ PyTypeObject *_Py_cobject_hack = &PyCObject_Type; diff --git a/Objects/unicodeobject.c b/Objects/unicodeobject.c index 82af3a2..4943413 100644 --- a/Objects/unicodeobject.c +++ b/Objects/unicodeobject.c @@ -2889,16 +2889,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/VS7.1/pythoncore.vcproj b/PC/VS7.1/pythoncore.vcproj index 98c260f..3ed5b8e 100644 --- a/PC/VS7.1/pythoncore.vcproj +++ b/PC/VS7.1/pythoncore.vcproj @@ -461,6 +461,9 @@ RelativePath="..\..\Objects\bytes_methods.c"> + + + + @@ -1419,6 +1423,10 @@ > + + diff --git a/PC/os2emx/python27.def b/PC/os2emx/python27.def index d1c38dc..5477cd2 100644 --- a/PC/os2emx/python27.def +++ b/PC/os2emx/python27.def @@ -210,6 +210,19 @@ EXPORTS "PyInstance_Type" "PyMethod_Type" +; From python27_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 python27_s.lib(cobject) "PyCObject_FromVoidPtr" "PyCObject_FromVoidPtrAndDesc" diff --git a/PC/os2vacpp/python.def b/PC/os2vacpp/python.def index e8064f3..76d56cd 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 @@ -75,6 +76,7 @@ EXPORTS _Py_TrueStruct _Py_ZeroStruct _Py_abstract_hack + _Py_capsule_hack _Py_cobject_hack _Py_re_syntax _Py_re_syntax_table @@ -89,6 +91,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/Python/compile.c b/Python/compile.c index 12210e1..90cf065 100644 --- a/Python/compile.c +++ b/Python/compile.c @@ -181,6 +181,8 @@ static int compiler_with(struct compiler *, stmt_ty); 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) { @@ -490,13 +492,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); } @@ -513,15 +515,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 287b5d9..b112650 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