From dcfe8719e9d77d19fd51a6f589835e676a4a89ef Mon Sep 17 00:00:00 2001 From: Christoph Reiter Date: Fri, 7 Nov 2025 22:39:13 +0100 Subject: [PATCH 01/13] enum_type_register_constant: add some more error handling in theory these can fail --- cairo/enums.c | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/cairo/enums.c b/cairo/enums.c index 4bbc88b4..9f546118 100644 --- a/cairo/enums.c +++ b/cairo/enums.c @@ -89,7 +89,12 @@ enum_type_register_constant(PyTypeObject *type, const char* name, long value) { value_map = PyDict_GetItemString(type->tp_dict, map_name); if (value_map == NULL) { value_map = PyDict_New(); - PyDict_SetItemString(type->tp_dict, map_name, value_map); + if (value_map == NULL) + return NULL; + if (PyDict_SetItemString(type->tp_dict, map_name, value_map) < 0) { + Py_DECREF(value_map); + return NULL; + } Py_DECREF(value_map); } From 1a4c5b348a0c514fc30a572c8397f902418310d5 Mon Sep 17 00:00:00 2001 From: Christoph Reiter Date: Fri, 7 Nov 2025 22:56:50 +0100 Subject: [PATCH 02/13] int_enum_get_name: add proper error handling and propagate errors return -1 on error, while still allowing NULL as a successfull result --- cairo/enums.c | 40 ++++++++++++++++++++++++++++++---------- 1 file changed, 30 insertions(+), 10 deletions(-) diff --git a/cairo/enums.c b/cairo/enums.c index 9f546118..241e499a 100644 --- a/cairo/enums.c +++ b/cairo/enums.c @@ -117,21 +117,39 @@ enum_type_register_constant(PyTypeObject *type, const char* name, long value) { return en; } -/* If returns NULL no error is set */ -static PyObject * -int_enum_get_name(PyObject *obj) { +/** + * Returns -1 on error with *result NULL, and 0 if successful. + * *result can be NULL if the name does not exist + */ +static int +int_enum_get_name(PyObject *obj, PyObject **result) { PyObject *value_map, *name_obj; + const char *name_str; value_map = PyDict_GetItemString(Py_TYPE(obj)->tp_dict, map_name); - if(value_map == NULL) - return NULL; + if(value_map == NULL) { + *result = NULL; + return 0; + } name_obj = PyDict_GetItem(value_map, obj); - if(name_obj == NULL) - return NULL; + if(name_obj == NULL) { + *result = NULL; + return 0; + } - return PyUnicode_FromFormat ("%s.%s", Py_TYPE(obj)->tp_name, - PyUnicode_AsUTF8(name_obj)); + name_str = PyUnicode_AsUTF8(name_obj); + if (name_str == NULL) { + *result = NULL; + return -1; + } + + *result = PyUnicode_FromFormat("%s.%s", Py_TYPE(obj)->tp_name, name_str); + if (*result == NULL) { + return -1; + } + + return 0; } static PyObject * @@ -139,7 +157,9 @@ int_enum_repr(PyObject *obj) { PyObject *name_obj; - name_obj = int_enum_get_name(obj); + if (int_enum_get_name(obj, &name_obj) < 0) { + return NULL; + } if(name_obj == NULL) return PyLong_Type.tp_repr(obj); From d6f9e1e3eec751a44de92b0894dbd24b635b98f3 Mon Sep 17 00:00:00 2001 From: Christoph Reiter Date: Fri, 7 Nov 2025 23:03:16 +0100 Subject: [PATCH 03/13] Vendor parts of pythoncapi_compat.h This vendors some new API functions from https://github.com/python/pythoncapi-compat/commit/e3efede9d842e5b11debdc732aca1192d833fd5b Not everything since it triggers various compiler warnings and we only need a few, so let's try that for now. --- cairo/private.h | 2 + cairo/pythoncapi_compat.h | 103 ++++++++++++++++++++++++++++++++++++++ 2 files changed, 105 insertions(+) create mode 100644 cairo/pythoncapi_compat.h diff --git a/cairo/private.h b/cairo/private.h index 812bbd22..6c2ce61d 100644 --- a/cairo/private.h +++ b/cairo/private.h @@ -41,6 +41,8 @@ #include "py3cairo.h" +#include "pythoncapi_compat.h" + #define PYCAIRO_STRINGIFY(s) PYCAIRO_STRINGIFY_ARG(s) #define PYCAIRO_STRINGIFY_ARG(s) #s diff --git a/cairo/pythoncapi_compat.h b/cairo/pythoncapi_compat.h new file mode 100644 index 00000000..7f8e5113 --- /dev/null +++ b/cairo/pythoncapi_compat.h @@ -0,0 +1,103 @@ +// Header file providing new C API functions to old Python versions. +// +// File distributed under the Zero Clause BSD (0BSD) license. +// Copyright Contributors to the pythoncapi_compat project. +// +// Homepage: +// https://github.com/python/pythoncapi_compat +// +// Latest version: +// https://raw.githubusercontent.com/python/pythoncapi-compat/main/pythoncapi_compat.h +// +// SPDX-License-Identifier: 0BSD + +#ifndef PYTHONCAPI_COMPAT +#define PYTHONCAPI_COMPAT + +#ifdef __cplusplus +extern "C" { +#endif + +#include + +// gh-106004 added PyDict_GetItemRef() and PyDict_GetItemStringRef() +// to Python 3.13.0a1 +#if PY_VERSION_HEX < 0x030D00A1 +static inline int +PyDict_GetItemRef(PyObject *mp, PyObject *key, PyObject **result) +{ +#if PY_VERSION_HEX >= 0x03000000 + PyObject *item = PyDict_GetItemWithError(mp, key); +#else + PyObject *item = _PyDict_GetItemWithError(mp, key); +#endif + if (item != NULL) { + *result = Py_NewRef(item); + return 1; // found + } + if (!PyErr_Occurred()) { + *result = NULL; + return 0; // not found + } + *result = NULL; + return -1; +} + +static inline int +PyDict_GetItemStringRef(PyObject *mp, const char *key, PyObject **result) +{ + int res; +#if PY_VERSION_HEX >= 0x03000000 + PyObject *key_obj = PyUnicode_FromString(key); +#else + PyObject *key_obj = PyString_FromString(key); +#endif + if (key_obj == NULL) { + *result = NULL; + return -1; + } + res = PyDict_GetItemRef(mp, key_obj, result); + Py_DECREF(key_obj); + return res; +} +#endif + +// bpo-1635741 added PyModule_AddObjectRef() to Python 3.10.0a3 +#if PY_VERSION_HEX < 0x030A00A3 +static inline int +PyModule_AddObjectRef(PyObject *module, const char *name, PyObject *value) +{ + int res; + + if (!value && !PyErr_Occurred()) { + // PyModule_AddObject() raises TypeError in this case + PyErr_SetString(PyExc_SystemError, + "PyModule_AddObjectRef() must be called " + "with an exception raised if value is NULL"); + return -1; + } + + Py_XINCREF(value); + res = PyModule_AddObject(module, name, value); + if (res < 0) { + Py_XDECREF(value); + } + return res; +} +#endif + +// gh-106307 added PyModule_Add() to Python 3.13.0a1 +#if PY_VERSION_HEX < 0x030D00A1 +static inline int +PyModule_Add(PyObject *mod, const char *name, PyObject *value) +{ + int res = PyModule_AddObjectRef(mod, name, value); + Py_XDECREF(value); + return res; +} +#endif + +#ifdef __cplusplus +} +#endif +#endif // PYTHONCAPI_COMPAT From e1240da2a9d01ac030d568c01d7a6c7274e1374f Mon Sep 17 00:00:00 2001 From: Christoph Reiter Date: Fri, 7 Nov 2025 23:22:19 +0100 Subject: [PATCH 04/13] enum: port away from PyDict_GetItem and PyDict_GetItemString Port to PyDict_GetItemRef and PyDict_GetItemStringRef so we get rid of borrowed references there. --- cairo/enums.c | 31 +++++++++++++++++++------------ 1 file changed, 19 insertions(+), 12 deletions(-) diff --git a/cairo/enums.c b/cairo/enums.c index 241e499a..d00e1abe 100644 --- a/cairo/enums.c +++ b/cairo/enums.c @@ -86,7 +86,9 @@ enum_type_register_constant(PyTypeObject *type, const char* name, long value) { PyObject *int_obj, *name_obj, *en; /* Get/Create the int->name mapping */ - value_map = PyDict_GetItemString(type->tp_dict, map_name); + if (PyDict_GetItemStringRef(type->tp_dict, map_name, &value_map) < 0) { + return NULL; + } if (value_map == NULL) { value_map = PyDict_New(); if (value_map == NULL) @@ -95,17 +97,18 @@ enum_type_register_constant(PyTypeObject *type, const char* name, long value) { Py_DECREF(value_map); return NULL; } - Py_DECREF(value_map); } /* Add int->name pair to the mapping */ int_obj = PyLong_FromLong(value); name_obj = PyUnicode_FromString (name); if (PyDict_SetItem(value_map, int_obj, name_obj) < 0) { + Py_DECREF(value_map); Py_DECREF(int_obj); Py_DECREF(name_obj); return NULL; } + Py_DECREF(value_map); Py_DECREF(int_obj); Py_DECREF(name_obj); @@ -124,30 +127,34 @@ enum_type_register_constant(PyTypeObject *type, const char* name, long value) { static int int_enum_get_name(PyObject *obj, PyObject **result) { PyObject *value_map, *name_obj; - const char *name_str; - value_map = PyDict_GetItemString(Py_TYPE(obj)->tp_dict, map_name); - if(value_map == NULL) { + if (PyDict_GetItemStringRef(Py_TYPE(obj)->tp_dict, map_name, &value_map) < 0) { *result = NULL; - return 0; + return -1; } - - name_obj = PyDict_GetItem(value_map, obj); - if(name_obj == NULL) { + if (value_map == NULL) { *result = NULL; return 0; } - name_str = PyUnicode_AsUTF8(name_obj); - if (name_str == NULL) { + if (PyDict_GetItemRef(value_map, obj, &name_obj) < 0) { + Py_DECREF(value_map); *result = NULL; return -1; } + Py_DECREF(value_map); - *result = PyUnicode_FromFormat("%s.%s", Py_TYPE(obj)->tp_name, name_str); + if (name_obj == NULL) { + *result = NULL; + return 0; + } + + *result = PyUnicode_FromFormat("%s.%S", Py_TYPE(obj)->tp_name, name_obj); if (*result == NULL) { + Py_DECREF(name_obj); return -1; } + Py_DECREF(name_obj); return 0; } From e46a59c8258138e6686fb30b5c8338e11fedb301 Mon Sep 17 00:00:00 2001 From: Christoph Reiter Date: Fri, 7 Nov 2025 23:43:39 +0100 Subject: [PATCH 05/13] Port away from deprecated PyModule_AddObject() Use PyModule_AddObjectRef() and PyModule_Add() instead. --- cairo/cairomodule.c | 112 +++++++++++++++----------------------------- cairo/enums.c | 5 +- cairo/error.c | 8 +--- 3 files changed, 42 insertions(+), 83 deletions(-) diff --git a/cairo/cairomodule.c b/cairo/cairomodule.c index d7d1716c..b9eb5e75 100644 --- a/cairo/cairomodule.c +++ b/cairo/cairomodule.c @@ -268,96 +268,68 @@ static int exec_cairo(PyObject *m) PYCAIRO_STRINGIFY(PYCAIRO_VERSION_MAJOR) "." PYCAIRO_STRINGIFY(PYCAIRO_VERSION_MINOR) "." PYCAIRO_STRINGIFY(PYCAIRO_VERSION_MICRO)); - PyModule_AddObject(m, "version_info", + PyModule_Add(m, "version_info", Py_BuildValue("(iii)", PYCAIRO_VERSION_MAJOR, PYCAIRO_VERSION_MINOR, PYCAIRO_VERSION_MICRO )); - Py_INCREF(&PycairoContext_Type); - PyModule_AddObject(m, "Context", (PyObject *)&PycairoContext_Type); - Py_INCREF(&PycairoFontFace_Type); - PyModule_AddObject(m, "FontFace",(PyObject *)&PycairoFontFace_Type); - Py_INCREF(&PycairoToyFontFace_Type); - PyModule_AddObject(m, "ToyFontFace",(PyObject *)&PycairoToyFontFace_Type); - Py_INCREF(&PycairoFontOptions_Type); - PyModule_AddObject(m, "FontOptions",(PyObject *)&PycairoFontOptions_Type); - Py_INCREF(&PycairoMatrix_Type); - PyModule_AddObject(m, "Matrix", (PyObject *)&PycairoMatrix_Type); - Py_INCREF(&PycairoPath_Type); + PyModule_AddObjectRef(m, "Context", (PyObject *)&PycairoContext_Type); + PyModule_AddObjectRef(m, "FontFace",(PyObject *)&PycairoFontFace_Type); + PyModule_AddObjectRef(m, "ToyFontFace",(PyObject *)&PycairoToyFontFace_Type); + PyModule_AddObjectRef(m, "FontOptions",(PyObject *)&PycairoFontOptions_Type); + PyModule_AddObjectRef(m, "Matrix", (PyObject *)&PycairoMatrix_Type); /* Don't add Path object since it is not accessed directly as 'cairo.Path' - * PyModule_AddObject(m, "Path", (PyObject *)&PycairoPath_Type); + * PyModule_AddObjectRef(m, "Path", (PyObject *)&PycairoPath_Type); */ - Py_INCREF(&PycairoPattern_Type); - PyModule_AddObject(m, "Pattern", (PyObject *)&PycairoPattern_Type); - Py_INCREF(&PycairoSolidPattern_Type); - PyModule_AddObject(m, "SolidPattern", + PyModule_AddObjectRef(m, "Pattern", (PyObject *)&PycairoPattern_Type); + PyModule_AddObjectRef(m, "SolidPattern", (PyObject *)&PycairoSolidPattern_Type); - Py_INCREF(&PycairoSurfacePattern_Type); - PyModule_AddObject(m, "SurfacePattern", + PyModule_AddObjectRef(m, "SurfacePattern", (PyObject *)&PycairoSurfacePattern_Type); - Py_INCREF(&PycairoGradient_Type); - PyModule_AddObject(m, "Gradient", (PyObject *)&PycairoGradient_Type); - Py_INCREF(&PycairoLinearGradient_Type); - PyModule_AddObject(m, "LinearGradient", + PyModule_AddObjectRef(m, "Gradient", (PyObject *)&PycairoGradient_Type); + PyModule_AddObjectRef(m, "LinearGradient", (PyObject *)&PycairoLinearGradient_Type); - Py_INCREF(&PycairoRadialGradient_Type); - PyModule_AddObject(m, "RadialGradient", + PyModule_AddObjectRef(m, "RadialGradient", (PyObject *)&PycairoRadialGradient_Type); - Py_INCREF(&PycairoRadialGradient_Type); - PyModule_AddObject(m, "MeshPattern", + PyModule_AddObjectRef(m, "MeshPattern", (PyObject *)&PycairoMeshPattern_Type); - Py_INCREF(&PycairoRasterSourcePattern_Type); - PyModule_AddObject(m, "RasterSourcePattern", + PyModule_AddObjectRef(m, "RasterSourcePattern", (PyObject *)&PycairoRasterSourcePattern_Type); - Py_INCREF(&PycairoRectangleInt_Type); - PyModule_AddObject(m, "RectangleInt", (PyObject *)&PycairoRectangleInt_Type); + PyModule_AddObjectRef(m, "RectangleInt", (PyObject *)&PycairoRectangleInt_Type); - Py_INCREF(&PycairoRegion_Type); - PyModule_AddObject(m, "Region", (PyObject *)&PycairoRegion_Type); + PyModule_AddObjectRef(m, "Region", (PyObject *)&PycairoRegion_Type); - Py_INCREF(&PycairoScaledFont_Type); - PyModule_AddObject(m, "ScaledFont", (PyObject *)&PycairoScaledFont_Type); + PyModule_AddObjectRef(m, "ScaledFont", (PyObject *)&PycairoScaledFont_Type); - Py_INCREF(&PycairoSurface_Type); - PyModule_AddObject(m, "Surface", (PyObject *)&PycairoSurface_Type); + PyModule_AddObjectRef(m, "Surface", (PyObject *)&PycairoSurface_Type); - Py_INCREF(&PycairoDevice_Type); - PyModule_AddObject(m, "Device", (PyObject *)&PycairoDevice_Type); + PyModule_AddObjectRef(m, "Device", (PyObject *)&PycairoDevice_Type); - Py_INCREF(&PycairoGlyph_Type); - PyModule_AddObject(m, "Glyph", (PyObject *)&PycairoGlyph_Type); + PyModule_AddObjectRef(m, "Glyph", (PyObject *)&PycairoGlyph_Type); - Py_INCREF(&PycairoRectangle_Type); - PyModule_AddObject(m, "Rectangle", (PyObject *)&PycairoRectangle_Type); + PyModule_AddObjectRef(m, "Rectangle", (PyObject *)&PycairoRectangle_Type); - Py_INCREF(&PycairoTextCluster_Type); - PyModule_AddObject(m, "TextCluster", (PyObject *)&PycairoTextCluster_Type); + PyModule_AddObjectRef(m, "TextCluster", (PyObject *)&PycairoTextCluster_Type); - Py_INCREF(&PycairoTextExtents_Type); - PyModule_AddObject(m, "TextExtents", (PyObject *)&PycairoTextExtents_Type); + PyModule_AddObjectRef(m, "TextExtents", (PyObject *)&PycairoTextExtents_Type); - Py_INCREF(&PycairoPath_Type); - PyModule_AddObject(m, "Path", (PyObject *)&PycairoPath_Type); + PyModule_AddObjectRef(m, "Path", (PyObject *)&PycairoPath_Type); #ifdef CAIRO_HAS_SCRIPT_SURFACE - Py_INCREF(&PycairoScriptDevice_Type); - PyModule_AddObject(m, "ScriptDevice", (PyObject *)&PycairoScriptDevice_Type); - Py_INCREF(&PycairoScriptSurface_Type); - PyModule_AddObject(m, "ScriptSurface", (PyObject *)&PycairoScriptSurface_Type); + PyModule_AddObjectRef(m, "ScriptDevice", (PyObject *)&PycairoScriptDevice_Type); + PyModule_AddObjectRef(m, "ScriptSurface", (PyObject *)&PycairoScriptSurface_Type); #endif #ifdef CAIRO_HAS_IMAGE_SURFACE - Py_INCREF(&PycairoImageSurface_Type); - PyModule_AddObject(m, "ImageSurface", + PyModule_AddObjectRef(m, "ImageSurface", (PyObject *)&PycairoImageSurface_Type); #endif #ifdef CAIRO_HAS_PDF_SURFACE - Py_INCREF(&PycairoPDFSurface_Type); - PyModule_AddObject(m, "PDFSurface", (PyObject *)&PycairoPDFSurface_Type); + PyModule_AddObjectRef(m, "PDFSurface", (PyObject *)&PycairoPDFSurface_Type); PyModule_AddIntConstant(m, "PDF_OUTLINE_ROOT", CAIRO_PDF_OUTLINE_ROOT); #endif @@ -366,45 +338,37 @@ static int exec_cairo(PyObject *m) #endif #ifdef CAIRO_HAS_PS_SURFACE - Py_INCREF(&PycairoPSSurface_Type); - PyModule_AddObject(m, "PSSurface", (PyObject *)&PycairoPSSurface_Type); + PyModule_AddObjectRef(m, "PSSurface", (PyObject *)&PycairoPSSurface_Type); #endif #ifdef CAIRO_HAS_RECORDING_SURFACE - Py_INCREF(&PycairoRecordingSurface_Type); - PyModule_AddObject(m, "RecordingSurface", + PyModule_AddObjectRef(m, "RecordingSurface", (PyObject *)&PycairoRecordingSurface_Type); #endif #ifdef CAIRO_HAS_SVG_SURFACE - Py_INCREF(&PycairoSVGSurface_Type); - PyModule_AddObject(m, "SVGSurface", (PyObject *)&PycairoSVGSurface_Type); + PyModule_AddObjectRef(m, "SVGSurface", (PyObject *)&PycairoSVGSurface_Type); #endif #ifdef CAIRO_HAS_WIN32_SURFACE - Py_INCREF(&PycairoWin32Surface_Type); - PyModule_AddObject(m, "Win32Surface", + PyModule_AddObjectRef(m, "Win32Surface", (PyObject *)&PycairoWin32Surface_Type); - Py_INCREF(&PycairoWin32PrintingSurface_Type); - PyModule_AddObject(m, "Win32PrintingSurface", + PyModule_AddObjectRef(m, "Win32PrintingSurface", (PyObject *)&PycairoWin32PrintingSurface_Type); #endif #ifdef CAIRO_HAS_XCB_SURFACE - Py_INCREF(&PycairoXCBSurface_Type); - PyModule_AddObject(m, "XCBSurface", + PyModule_AddObjectRef(m, "XCBSurface", (PyObject *)&PycairoXCBSurface_Type); #endif #ifdef CAIRO_HAS_XLIB_SURFACE - Py_INCREF(&PycairoXlibSurface_Type); - PyModule_AddObject(m, "XlibSurface", + PyModule_AddObjectRef(m, "XlibSurface", (PyObject *)&PycairoXlibSurface_Type); #endif #ifdef CAIRO_HAS_TEE_SURFACE - Py_INCREF(&PycairoTeeSurface_Type); - PyModule_AddObject(m, "TeeSurface", + PyModule_AddObjectRef(m, "TeeSurface", (PyObject *)&PycairoTeeSurface_Type); #endif @@ -538,7 +502,7 @@ static int exec_cairo(PyObject *m) capi = PyCapsule_New((void *)(&CAPI), "cairo.CAPI", 0); if (capi != NULL) { - PyModule_AddObject(m, "CAPI", capi); + PyModule_Add(m, "CAPI", capi); } else { return -1; } diff --git a/cairo/enums.c b/cairo/enums.c index d00e1abe..d6037e4c 100644 --- a/cairo/enums.c +++ b/cairo/enums.c @@ -251,8 +251,7 @@ init_enum_type (PyObject *module, const char *name, PyTypeObject *type) { if (PyType_Ready(type) < 0) return -1; - Py_INCREF(type); - if (PyModule_AddObject(module, name, (PyObject *)type) < 0) + if (PyModule_AddObjectRef(module, name, (PyObject *)type) < 0) return -1; return 0; @@ -306,7 +305,7 @@ init_enums (PyObject *module) { #define CONSTANT(t, a, b) \ ev = enum_type_register_constant(&Pycairo_##t##_Type, #b, CAIRO_##a##_##b); \ - if (ev == NULL || PyModule_AddObject(module, #a "_" #b, ev) < 0) \ + if (ev == NULL || PyModule_Add(module, #a "_" #b, ev) < 0) \ return -1; ENUM(Antialias); diff --git a/cairo/error.c b/cairo/error.c index 2cb37aac..d38bec9e 100644 --- a/cairo/error.c +++ b/cairo/error.c @@ -265,15 +265,11 @@ init_error (PyObject *module) { error = (PyObject*)&PycairoError_Type; - Py_INCREF(error); - if (PyModule_AddObject(module, "Error", error) < 0) { - Py_DECREF (error); + if (PyModule_AddObjectRef(module, "Error", error) < 0) { return -1; } - Py_INCREF(error); - if (PyModule_AddObject(module, "CairoError", error) < 0) { - Py_DECREF (error); + if (PyModule_AddObjectRef(module, "CairoError", error) < 0) { return -1; } From 0592b9779d0b6b681d22afd93f86e792d3a8dfd8 Mon Sep 17 00:00:00 2001 From: Christoph Reiter Date: Sat, 8 Nov 2025 18:48:46 +0100 Subject: [PATCH 06/13] cairomodule: add error handling to all PyModule_Add calls In theory they can all fail --- cairo/cairomodule.c | 314 ++++++++++++++++++++++++++++---------------- 1 file changed, 198 insertions(+), 116 deletions(-) diff --git a/cairo/cairomodule.c b/cairo/cairomodule.c index b9eb5e75..c180695c 100644 --- a/cairo/cairomodule.c +++ b/cairo/cairomodule.c @@ -264,218 +264,298 @@ static int exec_cairo(PyObject *m) if(init_enums(m) < 0) return -1; - PyModule_AddStringConstant(m, "version", - PYCAIRO_STRINGIFY(PYCAIRO_VERSION_MAJOR) "." - PYCAIRO_STRINGIFY(PYCAIRO_VERSION_MINOR) "." - PYCAIRO_STRINGIFY(PYCAIRO_VERSION_MICRO)); - PyModule_Add(m, "version_info", - Py_BuildValue("(iii)", - PYCAIRO_VERSION_MAJOR, - PYCAIRO_VERSION_MINOR, - PYCAIRO_VERSION_MICRO - )); - - PyModule_AddObjectRef(m, "Context", (PyObject *)&PycairoContext_Type); - PyModule_AddObjectRef(m, "FontFace",(PyObject *)&PycairoFontFace_Type); - PyModule_AddObjectRef(m, "ToyFontFace",(PyObject *)&PycairoToyFontFace_Type); - PyModule_AddObjectRef(m, "FontOptions",(PyObject *)&PycairoFontOptions_Type); - PyModule_AddObjectRef(m, "Matrix", (PyObject *)&PycairoMatrix_Type); + if (PyModule_AddStringConstant( + m, "version", + PYCAIRO_STRINGIFY(PYCAIRO_VERSION_MAJOR) "." PYCAIRO_STRINGIFY(PYCAIRO_VERSION_MINOR) "." PYCAIRO_STRINGIFY(PYCAIRO_VERSION_MICRO)) < 0) + return -1; + if (PyModule_Add(m, "version_info", + Py_BuildValue("(iii)", + PYCAIRO_VERSION_MAJOR, + PYCAIRO_VERSION_MINOR, + PYCAIRO_VERSION_MICRO)) < 0) + return -1; + + if (PyModule_AddObjectRef(m, "Context", (PyObject *)&PycairoContext_Type) < 0) + return -1; + if (PyModule_AddObjectRef(m, "FontFace",(PyObject *)&PycairoFontFace_Type) < 0) + return -1; + if (PyModule_AddObjectRef(m, "ToyFontFace",(PyObject *)&PycairoToyFontFace_Type) < 0) + return -1; + if (PyModule_AddObjectRef(m, "FontOptions",(PyObject *)&PycairoFontOptions_Type) < 0) + return -1; + if (PyModule_AddObjectRef(m, "Matrix", (PyObject *)&PycairoMatrix_Type) < 0) + return -1; /* Don't add Path object since it is not accessed directly as 'cairo.Path' * PyModule_AddObjectRef(m, "Path", (PyObject *)&PycairoPath_Type); */ - PyModule_AddObjectRef(m, "Pattern", (PyObject *)&PycairoPattern_Type); - PyModule_AddObjectRef(m, "SolidPattern", - (PyObject *)&PycairoSolidPattern_Type); - PyModule_AddObjectRef(m, "SurfacePattern", - (PyObject *)&PycairoSurfacePattern_Type); - PyModule_AddObjectRef(m, "Gradient", (PyObject *)&PycairoGradient_Type); - PyModule_AddObjectRef(m, "LinearGradient", - (PyObject *)&PycairoLinearGradient_Type); - PyModule_AddObjectRef(m, "RadialGradient", - (PyObject *)&PycairoRadialGradient_Type); - PyModule_AddObjectRef(m, "MeshPattern", - (PyObject *)&PycairoMeshPattern_Type); - PyModule_AddObjectRef(m, "RasterSourcePattern", - (PyObject *)&PycairoRasterSourcePattern_Type); - - PyModule_AddObjectRef(m, "RectangleInt", (PyObject *)&PycairoRectangleInt_Type); - - PyModule_AddObjectRef(m, "Region", (PyObject *)&PycairoRegion_Type); - - PyModule_AddObjectRef(m, "ScaledFont", (PyObject *)&PycairoScaledFont_Type); - - PyModule_AddObjectRef(m, "Surface", (PyObject *)&PycairoSurface_Type); - - PyModule_AddObjectRef(m, "Device", (PyObject *)&PycairoDevice_Type); - - PyModule_AddObjectRef(m, "Glyph", (PyObject *)&PycairoGlyph_Type); - - PyModule_AddObjectRef(m, "Rectangle", (PyObject *)&PycairoRectangle_Type); - - PyModule_AddObjectRef(m, "TextCluster", (PyObject *)&PycairoTextCluster_Type); - - PyModule_AddObjectRef(m, "TextExtents", (PyObject *)&PycairoTextExtents_Type); - - PyModule_AddObjectRef(m, "Path", (PyObject *)&PycairoPath_Type); + if (PyModule_AddObjectRef(m, "Pattern", (PyObject *)&PycairoPattern_Type) < 0) + return -1; + if (PyModule_AddObjectRef(m, "SolidPattern", + (PyObject *)&PycairoSolidPattern_Type) < 0) + return -1; + if (PyModule_AddObjectRef(m, "SurfacePattern", + (PyObject *)&PycairoSurfacePattern_Type) < 0) + return -1; + if (PyModule_AddObjectRef(m, "Gradient", (PyObject *)&PycairoGradient_Type) < 0) + return -1; + if (PyModule_AddObjectRef(m, "LinearGradient", + (PyObject *)&PycairoLinearGradient_Type) < 0) + return -1; + if (PyModule_AddObjectRef(m, "RadialGradient", + (PyObject *)&PycairoRadialGradient_Type) < 0) + return -1; + if (PyModule_AddObjectRef(m, "MeshPattern", + (PyObject *)&PycairoMeshPattern_Type) < 0) + return -1; + if (PyModule_AddObjectRef(m, "RasterSourcePattern", + (PyObject *)&PycairoRasterSourcePattern_Type) < 0) + return -1; + + if (PyModule_AddObjectRef(m, "RectangleInt", (PyObject *)&PycairoRectangleInt_Type) < 0) + return -1; + + if (PyModule_AddObjectRef(m, "Region", (PyObject *)&PycairoRegion_Type) < 0) + return -1; + + if (PyModule_AddObjectRef(m, "ScaledFont", (PyObject *)&PycairoScaledFont_Type) < 0) + return -1; + + if (PyModule_AddObjectRef(m, "Surface", (PyObject *)&PycairoSurface_Type) < 0) + return -1; + + if (PyModule_AddObjectRef(m, "Device", (PyObject *)&PycairoDevice_Type) < 0) + return -1; + + if (PyModule_AddObjectRef(m, "Glyph", (PyObject *)&PycairoGlyph_Type) < 0) + return -1; + + if (PyModule_AddObjectRef(m, "Rectangle", (PyObject *)&PycairoRectangle_Type) < 0) + return -1; + + if (PyModule_AddObjectRef(m, "TextCluster", (PyObject *)&PycairoTextCluster_Type) < 0) + return -1; + + if (PyModule_AddObjectRef(m, "TextExtents", (PyObject *)&PycairoTextExtents_Type) < 0) + return -1; + + if (PyModule_AddObjectRef(m, "Path", (PyObject *)&PycairoPath_Type) < 0) + return -1; #ifdef CAIRO_HAS_SCRIPT_SURFACE - PyModule_AddObjectRef(m, "ScriptDevice", (PyObject *)&PycairoScriptDevice_Type); - PyModule_AddObjectRef(m, "ScriptSurface", (PyObject *)&PycairoScriptSurface_Type); + if (PyModule_AddObjectRef(m, "ScriptDevice", (PyObject *)&PycairoScriptDevice_Type) < 0) + return -1; + if (PyModule_AddObjectRef(m, "ScriptSurface", (PyObject *)&PycairoScriptSurface_Type) < 0) + return -1; #endif #ifdef CAIRO_HAS_IMAGE_SURFACE - PyModule_AddObjectRef(m, "ImageSurface", - (PyObject *)&PycairoImageSurface_Type); + if (PyModule_AddObjectRef(m, "ImageSurface", + (PyObject *)&PycairoImageSurface_Type) < 0) + return -1; #endif #ifdef CAIRO_HAS_PDF_SURFACE - PyModule_AddObjectRef(m, "PDFSurface", (PyObject *)&PycairoPDFSurface_Type); - PyModule_AddIntConstant(m, "PDF_OUTLINE_ROOT", CAIRO_PDF_OUTLINE_ROOT); + if (PyModule_AddObjectRef(m, "PDFSurface", (PyObject *)&PycairoPDFSurface_Type) < 0) + return -1; + if (PyModule_AddIntConstant(m, "PDF_OUTLINE_ROOT", CAIRO_PDF_OUTLINE_ROOT) < 0) + return -1; #endif #if CAIRO_VERSION >= CAIRO_VERSION_ENCODE(1, 17, 8) - PyModule_AddIntConstant(m, "COLOR_PALETTE_DEFAULT", CAIRO_COLOR_PALETTE_DEFAULT); + if (PyModule_AddIntConstant(m, "COLOR_PALETTE_DEFAULT", CAIRO_COLOR_PALETTE_DEFAULT) < 0) + return -1; #endif #ifdef CAIRO_HAS_PS_SURFACE - PyModule_AddObjectRef(m, "PSSurface", (PyObject *)&PycairoPSSurface_Type); + if (PyModule_AddObjectRef(m, "PSSurface", (PyObject *)&PycairoPSSurface_Type) < 0) + return -1; #endif #ifdef CAIRO_HAS_RECORDING_SURFACE - PyModule_AddObjectRef(m, "RecordingSurface", - (PyObject *)&PycairoRecordingSurface_Type); + if (PyModule_AddObjectRef(m, "RecordingSurface", + (PyObject *)&PycairoRecordingSurface_Type) < 0) + return -1; #endif #ifdef CAIRO_HAS_SVG_SURFACE - PyModule_AddObjectRef(m, "SVGSurface", (PyObject *)&PycairoSVGSurface_Type); + if (PyModule_AddObjectRef(m, "SVGSurface", (PyObject *)&PycairoSVGSurface_Type) < 0) + return -1; #endif #ifdef CAIRO_HAS_WIN32_SURFACE - PyModule_AddObjectRef(m, "Win32Surface", - (PyObject *)&PycairoWin32Surface_Type); - PyModule_AddObjectRef(m, "Win32PrintingSurface", - (PyObject *)&PycairoWin32PrintingSurface_Type); + if (PyModule_AddObjectRef(m, "Win32Surface", + (PyObject *)&PycairoWin32Surface_Type) < 0) + return -1; + if (PyModule_AddObjectRef(m, "Win32PrintingSurface", + (PyObject *)&PycairoWin32PrintingSurface_Type) < 0) + return -1; #endif #ifdef CAIRO_HAS_XCB_SURFACE - PyModule_AddObjectRef(m, "XCBSurface", - (PyObject *)&PycairoXCBSurface_Type); + if (PyModule_AddObjectRef(m, "XCBSurface", + (PyObject *)&PycairoXCBSurface_Type) < 0) + return -1; #endif #ifdef CAIRO_HAS_XLIB_SURFACE - PyModule_AddObjectRef(m, "XlibSurface", - (PyObject *)&PycairoXlibSurface_Type); + if (PyModule_AddObjectRef(m, "XlibSurface", + (PyObject *)&PycairoXlibSurface_Type) < 0) + return -1; #endif #ifdef CAIRO_HAS_TEE_SURFACE - PyModule_AddObjectRef(m, "TeeSurface", - (PyObject *)&PycairoTeeSurface_Type); + if (PyModule_AddObjectRef(m, "TeeSurface", + (PyObject *)&PycairoTeeSurface_Type) < 0) + return -1; #endif /* constants */ #ifdef CAIRO_HAS_ATSUI_FONT - PyModule_AddIntConstant(m, "HAS_ATSUI_FONT", 1); + if (PyModule_AddIntConstant(m, "HAS_ATSUI_FONT", 1) < 0) + return -1; #else - PyModule_AddIntConstant(m, "HAS_ATSUI_FONT", 0); + if (PyModule_AddIntConstant(m, "HAS_ATSUI_FONT", 0) < 0) + return -1; #endif #ifdef CAIRO_HAS_FT_FONT - PyModule_AddIntConstant(m, "HAS_FT_FONT", 1); + if (PyModule_AddIntConstant(m, "HAS_FT_FONT", 1) < 0) + return -1; #else - PyModule_AddIntConstant(m, "HAS_FT_FONT", 0); + if (PyModule_AddIntConstant(m, "HAS_FT_FONT", 0) < 0) + return -1; #endif #ifdef CAIRO_HAS_GLITZ_SURFACE - PyModule_AddIntConstant(m, "HAS_GLITZ_SURFACE", 1); + if (PyModule_AddIntConstant(m, "HAS_GLITZ_SURFACE", 1) < 0) + return -1; #else - PyModule_AddIntConstant(m, "HAS_GLITZ_SURFACE", 0); + if (PyModule_AddIntConstant(m, "HAS_GLITZ_SURFACE", 0) < 0) + return -1; #endif #ifdef CAIRO_HAS_IMAGE_SURFACE - PyModule_AddIntConstant(m, "HAS_IMAGE_SURFACE", 1); + if (PyModule_AddIntConstant(m, "HAS_IMAGE_SURFACE", 1) < 0) + return -1; #else - PyModule_AddIntConstant(m, "HAS_IMAGE_SURFACE", 0); + if (PyModule_AddIntConstant(m, "HAS_IMAGE_SURFACE", 0) < 0) + return -1; #endif #ifdef CAIRO_HAS_PDF_SURFACE - PyModule_AddIntConstant(m, "HAS_PDF_SURFACE", 1); + if (PyModule_AddIntConstant(m, "HAS_PDF_SURFACE", 1) < 0) + return -1; #else - PyModule_AddIntConstant(m, "HAS_PDF_SURFACE", 0); + if (PyModule_AddIntConstant(m, "HAS_PDF_SURFACE", 0) < 0) + return -1; #endif #ifdef CAIRO_HAS_PNG_FUNCTIONS - PyModule_AddIntConstant(m, "HAS_PNG_FUNCTIONS", 1); + if (PyModule_AddIntConstant(m, "HAS_PNG_FUNCTIONS", 1) < 0) + return -1; #else - PyModule_AddIntConstant(m, "HAS_PNG_FUNCTIONS", 0); + if (PyModule_AddIntConstant(m, "HAS_PNG_FUNCTIONS", 0) < 0) + return -1; #endif #ifdef CAIRO_HAS_PS_SURFACE - PyModule_AddIntConstant(m, "HAS_PS_SURFACE", 1); + if (PyModule_AddIntConstant(m, "HAS_PS_SURFACE", 1) < 0) + return -1; #else - PyModule_AddIntConstant(m, "HAS_PS_SURFACE", 0); + if (PyModule_AddIntConstant(m, "HAS_PS_SURFACE", 0) < 0) + return -1; #endif #ifdef CAIRO_HAS_RECORDING_SURFACE - PyModule_AddIntConstant(m, "HAS_RECORDING_SURFACE", 1); + if (PyModule_AddIntConstant(m, "HAS_RECORDING_SURFACE", 1) < 0) + return -1; #else - PyModule_AddIntConstant(m, "HAS_RECORDING_SURFACE", 0); + if (PyModule_AddIntConstant(m, "HAS_RECORDING_SURFACE", 0) < 0) + return -1; #endif #ifdef CAIRO_HAS_SVG_SURFACE - PyModule_AddIntConstant(m, "HAS_SVG_SURFACE", 1); + if (PyModule_AddIntConstant(m, "HAS_SVG_SURFACE", 1) < 0) + return -1; #else - PyModule_AddIntConstant(m, "HAS_SVG_SURFACE", 0); + if (PyModule_AddIntConstant(m, "HAS_SVG_SURFACE", 0) < 0) + return -1; #endif #ifdef CAIRO_HAS_USER_FONT - PyModule_AddIntConstant(m, "HAS_USER_FONT", 1); + if (PyModule_AddIntConstant(m, "HAS_USER_FONT", 1) < 0) + return -1; #else - PyModule_AddIntConstant(m, "HAS_USER_FONT", 0); + if (PyModule_AddIntConstant(m, "HAS_USER_FONT", 0) < 0) + return -1; #endif #ifdef CAIRO_HAS_QUARTZ_SURFACE - PyModule_AddIntConstant(m, "HAS_QUARTZ_SURFACE", 1); + if (PyModule_AddIntConstant(m, "HAS_QUARTZ_SURFACE", 1) < 0) + return -1; #else - PyModule_AddIntConstant(m, "HAS_QUARTZ_SURFACE", 0); + if (PyModule_AddIntConstant(m, "HAS_QUARTZ_SURFACE", 0) < 0) + return -1; #endif #ifdef CAIRO_HAS_WIN32_FONT - PyModule_AddIntConstant(m, "HAS_WIN32_FONT", 1); + if (PyModule_AddIntConstant(m, "HAS_WIN32_FONT", 1) < 0) + return -1; #else - PyModule_AddIntConstant(m, "HAS_WIN32_FONT", 0); + if (PyModule_AddIntConstant(m, "HAS_WIN32_FONT", 0) < 0) + return -1; #endif #ifdef CAIRO_HAS_WIN32_SURFACE - PyModule_AddIntConstant(m, "HAS_WIN32_SURFACE", 1); + if (PyModule_AddIntConstant(m, "HAS_WIN32_SURFACE", 1) < 0) + return -1; #else - PyModule_AddIntConstant(m, "HAS_WIN32_SURFACE", 0); + if (PyModule_AddIntConstant(m, "HAS_WIN32_SURFACE", 0) < 0) + return -1; #endif #ifdef CAIRO_HAS_XCB_SURFACE - PyModule_AddIntConstant(m, "HAS_XCB_SURFACE", 1); + if (PyModule_AddIntConstant(m, "HAS_XCB_SURFACE", 1) < 0) + return -1; #else - PyModule_AddIntConstant(m, "HAS_XCB_SURFACE", 0); + if (PyModule_AddIntConstant(m, "HAS_XCB_SURFACE", 0) < 0) + return -1; #endif #ifdef CAIRO_HAS_XLIB_SURFACE - PyModule_AddIntConstant(m, "HAS_XLIB_SURFACE", 1); + if (PyModule_AddIntConstant(m, "HAS_XLIB_SURFACE", 1) < 0) + return -1; #else - PyModule_AddIntConstant(m, "HAS_XLIB_SURFACE", 0); + if (PyModule_AddIntConstant(m, "HAS_XLIB_SURFACE", 0) < 0) + return -1; #endif #ifdef CAIRO_HAS_MIME_SURFACE - PyModule_AddIntConstant(m, "HAS_MIME_SURFACE", 1); + if (PyModule_AddIntConstant(m, "HAS_MIME_SURFACE", 1) < 0) + return -1; #else - PyModule_AddIntConstant(m, "HAS_MIME_SURFACE", 0); + if (PyModule_AddIntConstant(m, "HAS_MIME_SURFACE", 0) < 0) + return -1; #endif #ifdef CAIRO_HAS_SCRIPT_SURFACE - PyModule_AddIntConstant(m, "HAS_SCRIPT_SURFACE", 1); + if (PyModule_AddIntConstant(m, "HAS_SCRIPT_SURFACE", 1) < 0) + return -1; #else - PyModule_AddIntConstant(m, "HAS_SCRIPT_SURFACE", 0); + if (PyModule_AddIntConstant(m, "HAS_SCRIPT_SURFACE", 0) < 0) + return -1; #endif #ifdef CAIRO_HAS_TEE_SURFACE - PyModule_AddIntConstant(m, "HAS_TEE_SURFACE", 1); + if (PyModule_AddIntConstant(m, "HAS_TEE_SURFACE", 1) < 0) + return -1; #else - PyModule_AddIntConstant(m, "HAS_TEE_SURFACE", 0); + if (PyModule_AddIntConstant(m, "HAS_TEE_SURFACE", 0) < 0) + return -1; #endif #ifdef CAIRO_HAS_DWRITE_FONT - PyModule_AddIntConstant(m, "HAS_DWRITE_FONT", 1); + if (PyModule_AddIntConstant(m, "HAS_DWRITE_FONT", 1) < 0) + return -1; #else - PyModule_AddIntConstant(m, "HAS_DWRITE_FONT", 0); + if (PyModule_AddIntConstant(m, "HAS_DWRITE_FONT", 0) < 0) + return -1; #endif - PyModule_AddIntConstant(m, "CAIRO_VERSION", CAIRO_VERSION); - PyModule_AddIntConstant(m, "CAIRO_VERSION_MAJOR", CAIRO_VERSION_MAJOR); - PyModule_AddIntConstant(m, "CAIRO_VERSION_MICRO", CAIRO_VERSION_MICRO); - PyModule_AddIntConstant(m, "CAIRO_VERSION_MINOR", CAIRO_VERSION_MINOR); - PyModule_AddStringConstant(m, "CAIRO_VERSION_STRING", CAIRO_VERSION_STRING); + if (PyModule_AddIntConstant(m, "CAIRO_VERSION", CAIRO_VERSION) < 0) + return -1; + if (PyModule_AddIntConstant(m, "CAIRO_VERSION_MAJOR", CAIRO_VERSION_MAJOR) < 0) + return -1; + if (PyModule_AddIntConstant(m, "CAIRO_VERSION_MICRO", CAIRO_VERSION_MICRO) < 0) + return -1; + if (PyModule_AddIntConstant(m, "CAIRO_VERSION_MINOR", CAIRO_VERSION_MINOR) < 0) + return -1; + if (PyModule_AddStringConstant(m, "CAIRO_VERSION_STRING", CAIRO_VERSION_STRING) < 0) + return -1; -#define STRCONSTANT(x) PyModule_AddStringConstant(m, #x, CAIRO_##x) +#define STRCONSTANT(x) if (PyModule_AddStringConstant(m, #x, CAIRO_##x) < 0) return -1; STRCONSTANT(MIME_TYPE_JP2); STRCONSTANT(MIME_TYPE_JPEG); @@ -502,7 +582,9 @@ static int exec_cairo(PyObject *m) capi = PyCapsule_New((void *)(&CAPI), "cairo.CAPI", 0); if (capi != NULL) { - PyModule_Add(m, "CAPI", capi); + if (PyModule_Add(m, "CAPI", capi) < 0) { + return -1; + } } else { return -1; } From e93072fca2d986e25b99187e0b83e4889894ff1d Mon Sep 17 00:00:00 2001 From: Christoph Reiter Date: Sat, 8 Nov 2025 19:01:48 +0100 Subject: [PATCH 07/13] _PyTextCluster_AsTextCluster: port from PySequence_Fast_GET_ITEM to PySequence_ITEM To avoid working with borrowed references --- cairo/textcluster.c | 21 +++++++++++++++------ 1 file changed, 15 insertions(+), 6 deletions(-) diff --git a/cairo/textcluster.c b/cairo/textcluster.c index 5d4eb2e2..51726179 100644 --- a/cairo/textcluster.c +++ b/cairo/textcluster.c @@ -37,6 +37,7 @@ int _PyTextCluster_AsTextCluster (PyObject *pyobj, cairo_text_cluster_t *cluster) { long num_bytes, num_glyphs; + PyObject *py_num_bytes, *py_num_glyphs; if (!PyObject_TypeCheck (pyobj, &PycairoTextCluster_Type)) { PyErr_SetString (PyExc_TypeError, @@ -44,9 +45,13 @@ _PyTextCluster_AsTextCluster (PyObject *pyobj, cairo_text_cluster_t *cluster) { return -1; } - num_bytes = PyLong_AsLong ( - PySequence_Fast_GET_ITEM (pyobj, 0)); - if (PyErr_Occurred ()) + py_num_bytes = PySequence_ITEM (pyobj, 0); + if (py_num_bytes == NULL) + return -1; + + num_bytes = PyLong_AsLong (py_num_bytes); + Py_DECREF (py_num_bytes); + if (num_bytes == -1 && PyErr_Occurred ()) return -1; if (num_bytes > INT_MAX || num_bytes < INT_MIN) { PyErr_SetString (PyExc_ValueError, "num_bytes out of range"); @@ -54,9 +59,13 @@ _PyTextCluster_AsTextCluster (PyObject *pyobj, cairo_text_cluster_t *cluster) { } cluster->num_bytes = (int)num_bytes; - num_glyphs = PyLong_AsLong ( - PySequence_Fast_GET_ITEM (pyobj, 1)); - if (PyErr_Occurred ()) + py_num_glyphs = PySequence_ITEM (pyobj, 1); + if (py_num_glyphs == NULL) + return -1; + + num_glyphs = PyLong_AsLong (py_num_glyphs); + Py_DECREF (py_num_glyphs); + if (num_glyphs == -1 && PyErr_Occurred ()) return -1; if (num_glyphs > INT_MAX || num_glyphs < INT_MIN) { PyErr_SetString (PyExc_ValueError, "num_glyphs out of range"); From 7e440c0e4f7d5638aa5a7cd9ced1c367fa2cfac1 Mon Sep 17 00:00:00 2001 From: Christoph Reiter Date: Sat, 8 Nov 2025 19:15:38 +0100 Subject: [PATCH 08/13] glyph: correctly convert index to unsigned long index is unsigned, so make sure we don't allow negative numbers there. --- cairo/glyph.c | 20 ++++++++------------ 1 file changed, 8 insertions(+), 12 deletions(-) diff --git a/cairo/glyph.c b/cairo/glyph.c index 26b34439..589c1da9 100644 --- a/cairo/glyph.c +++ b/cairo/glyph.c @@ -65,7 +65,7 @@ _PycairoGlyphs_AsGlyphs (PyObject *py_object, int *num_glyphs) goto error; } for (i = 0, glyph = glyphs; i < *num_glyphs; i++, glyph++) { - long index; + unsigned long index; PyObject *py_item = PySequence_Fast_GET_ITEM (py_glyphs, i); py_seq = PySequence_Fast (py_item, "glyph items must be a sequence"); if (py_seq == NULL) @@ -75,10 +75,10 @@ _PycairoGlyphs_AsGlyphs (PyObject *py_object, int *num_glyphs) "each glyph item must be an (i,x,y) sequence"); goto error; } - index = PyLong_AsLong (PySequence_Fast_GET_ITEM (py_seq, 0)); - if (PyErr_Occurred ()) + index = PyLong_AsUnsignedLong (PySequence_Fast_GET_ITEM (py_seq, 0)); + if (index == (unsigned long)-1 && PyErr_Occurred ()) goto error; - glyph->index = (unsigned long)index; + glyph->index = index; glyph->x = PyFloat_AsDouble (PySequence_Fast_GET_ITEM (py_seq, 1)); glyph->y = PyFloat_AsDouble (PySequence_Fast_GET_ITEM (py_seq, 2)); if (PyErr_Occurred()) @@ -99,21 +99,17 @@ _PycairoGlyphs_AsGlyphs (PyObject *py_object, int *num_glyphs) /* 0 on success */ int _PyGlyph_AsGlyph (PyObject *pyobj, cairo_glyph_t *glyph) { - long index; + unsigned long index; if (!PyObject_TypeCheck (pyobj, &PycairoGlyph_Type)) { PyErr_SetString (PyExc_TypeError, "item must be of type cairo.Glyph"); return -1; } - index = PyLong_AsLong (PySequence_Fast_GET_ITEM (pyobj, 0)); - if (PyErr_Occurred ()) - return -1; - if (index < 0) { - PyErr_SetString (PyExc_ValueError, "negative index"); + index = PyLong_AsUnsignedLong (PySequence_Fast_GET_ITEM (pyobj, 0)); + if (index == (unsigned long)-1 && PyErr_Occurred ()) return -1; - } - glyph->index = (unsigned long) index; + glyph->index = index; glyph->x = PyFloat_AsDouble (PySequence_Fast_GET_ITEM (pyobj, 1)); glyph->y = PyFloat_AsDouble (PySequence_Fast_GET_ITEM (pyobj, 2)); return 0; From ef996c1855b7e3631caad4988050c594e4492aeb Mon Sep 17 00:00:00 2001 From: Christoph Reiter Date: Sat, 8 Nov 2025 20:07:48 +0100 Subject: [PATCH 09/13] context: port from PySequence_Fast_GET_ITEM to PySequence_ITEM to avoid borrowed references --- cairo/context.c | 28 ++++++++++++++++++++-------- 1 file changed, 20 insertions(+), 8 deletions(-) diff --git a/cairo/context.c b/cairo/context.c index 2d1549e6..08ed5ca5 100644 --- a/cairo/context.c +++ b/cairo/context.c @@ -893,7 +893,14 @@ pycairo_set_dash (PycairoContext *o, PyObject *args) { } for (i = 0; i < num_dashes; i++) { - dashes[i] = PyFloat_AsDouble(PySequence_Fast_GET_ITEM(py_dashes, i)); + PyObject *py_dash = PySequence_ITEM(py_dashes, i); + if (py_dash == NULL) { + PyMem_Free (dashes); + Py_DECREF(py_dashes); + return NULL; + } + dashes[i] = PyFloat_AsDouble(py_dash); + Py_DECREF(py_dash); if (PyErr_Occurred()) { PyMem_Free (dashes); Py_DECREF(py_dashes); @@ -1328,9 +1335,12 @@ pycairo_show_text_glyphs (PycairoContext *o, PyObject *args) { goto error; } for (i=0; i < glyphs_size; i++) { - py_item = PySequence_Fast_GET_ITEM (glyphs_seq, i); - if (py_item == 0 || _PyGlyph_AsGlyph (py_item, &(glyphs[i])) != 0) - goto error; + py_item = PySequence_ITEM (glyphs_seq, i); + if (py_item == NULL || _PyGlyph_AsGlyph (py_item, &(glyphs[i])) != 0) { + Py_XDECREF (py_item); + goto error; + } + Py_DECREF (py_item); } Py_CLEAR (glyphs_seq); @@ -1348,10 +1358,12 @@ pycairo_show_text_glyphs (PycairoContext *o, PyObject *args) { goto error; } for (i=0; i < clusters_size; i++) { - py_item = PySequence_Fast_GET_ITEM (clusters_seq, i); - if (py_item == NULL || - _PyTextCluster_AsTextCluster (py_item, &(clusters[i])) != 0) - goto error; + py_item = PySequence_ITEM (clusters_seq, i); + if (py_item == NULL || _PyTextCluster_AsTextCluster (py_item, &(clusters[i])) != 0) { + Py_XDECREF (py_item); + goto error; + } + Py_DECREF (py_item); } Py_CLEAR (clusters_seq); From 2e55a79ddde575593f9b304dc98af0ac1992f396 Mon Sep 17 00:00:00 2001 From: Christoph Reiter Date: Sat, 8 Nov 2025 20:10:39 +0100 Subject: [PATCH 10/13] region: port from PySequence_Fast_GET_ITEM to PySequence_ITEM to avoid borrowed references --- cairo/region.c | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/cairo/region.c b/cairo/region.c index 879b9e02..b2ba786c 100644 --- a/cairo/region.c +++ b/cairo/region.c @@ -233,8 +233,9 @@ region_new(PyTypeObject *type, PyObject *args, PyObject *kwds) { } for(i=0; irectangle_int; + Py_DECREF(obj_tmp); } region = cairo_region_create_rectangles(rect, (int)rect_size); From 05f811a5a6209348749518ad990c3c42c4c885fe Mon Sep 17 00:00:00 2001 From: Christoph Reiter Date: Sat, 8 Nov 2025 20:38:58 +0100 Subject: [PATCH 11/13] glyph: port from PySequence_Fast_GET_ITEM to PySequence_ITEM to avoid borrowed references --- cairo/glyph.c | 74 ++++++++++++++++++++++++++++++++++++++++++++------- 1 file changed, 64 insertions(+), 10 deletions(-) diff --git a/cairo/glyph.c b/cairo/glyph.c index 589c1da9..b2872c08 100644 --- a/cairo/glyph.c +++ b/cairo/glyph.c @@ -66,8 +66,12 @@ _PycairoGlyphs_AsGlyphs (PyObject *py_object, int *num_glyphs) } for (i = 0, glyph = glyphs; i < *num_glyphs; i++, glyph++) { unsigned long index; - PyObject *py_item = PySequence_Fast_GET_ITEM (py_glyphs, i); + double x, y; + PyObject *py_item = PySequence_ITEM (py_glyphs, i); + if (py_item == NULL) + goto error; py_seq = PySequence_Fast (py_item, "glyph items must be a sequence"); + Py_DECREF (py_item); if (py_seq == NULL) goto error; if (PySequence_Fast_GET_SIZE (py_seq) != 3) { @@ -75,14 +79,38 @@ _PycairoGlyphs_AsGlyphs (PyObject *py_object, int *num_glyphs) "each glyph item must be an (i,x,y) sequence"); goto error; } - index = PyLong_AsUnsignedLong (PySequence_Fast_GET_ITEM (py_seq, 0)); - if (index == (unsigned long)-1 && PyErr_Occurred ()) + PyObject *py_index = PySequence_ITEM (py_seq, 0); + if (py_index == NULL) { goto error; - glyph->index = index; - glyph->x = PyFloat_AsDouble (PySequence_Fast_GET_ITEM (py_seq, 1)); - glyph->y = PyFloat_AsDouble (PySequence_Fast_GET_ITEM (py_seq, 2)); - if (PyErr_Occurred()) + } + index = PyLong_AsUnsignedLong (py_index); + Py_DECREF (py_index); + if (index == (unsigned long)-1 && PyErr_Occurred ()) { + goto error; + } + PyObject *py_x = PySequence_ITEM (py_seq, 1); + if (py_x == NULL) { + goto error; + } + x = PyFloat_AsDouble (py_x); + Py_DECREF (py_x); + if (PyErr_Occurred()) { goto error; + } + PyObject *py_y = PySequence_ITEM (py_seq, 2); + if (py_y == NULL) { + goto error; + } + y = PyFloat_AsDouble (py_y); + Py_DECREF (py_y); + if (PyErr_Occurred()) { + goto error; + } + + glyph->index = index; + glyph->x = x; + glyph->y = y; + Py_DECREF (py_seq); } @@ -100,18 +128,44 @@ _PycairoGlyphs_AsGlyphs (PyObject *py_object, int *num_glyphs) int _PyGlyph_AsGlyph (PyObject *pyobj, cairo_glyph_t *glyph) { unsigned long index; + double x, y; if (!PyObject_TypeCheck (pyobj, &PycairoGlyph_Type)) { PyErr_SetString (PyExc_TypeError, "item must be of type cairo.Glyph"); return -1; } - index = PyLong_AsUnsignedLong (PySequence_Fast_GET_ITEM (pyobj, 0)); + PyObject *py_index = PySequence_ITEM (pyobj, 0); + if (py_index == NULL) { + return -1; + } + index = PyLong_AsUnsignedLong (py_index); + Py_DECREF (py_index); if (index == (unsigned long)-1 && PyErr_Occurred ()) return -1; + PyObject *py_x = PySequence_ITEM (pyobj, 1); + if (py_x == NULL) { + return -1; + } + x = PyFloat_AsDouble (py_x); + Py_DECREF (py_x); + if (PyErr_Occurred()) { + return -1; + } + PyObject *py_y = PySequence_ITEM (pyobj, 2); + if (py_y == NULL) { + return -1; + } + y = PyFloat_AsDouble (py_y); + Py_DECREF (py_y); + if (PyErr_Occurred()) { + return -1; + } + glyph->index = index; - glyph->x = PyFloat_AsDouble (PySequence_Fast_GET_ITEM (pyobj, 1)); - glyph->y = PyFloat_AsDouble (PySequence_Fast_GET_ITEM (pyobj, 2)); + glyph->x = x; + glyph->y = y; + return 0; } From 35f588471565e7aed5545f64f999d2b7ad5e6fb8 Mon Sep 17 00:00:00 2001 From: Christoph Reiter Date: Sun, 9 Nov 2025 08:34:08 +0100 Subject: [PATCH 12/13] Improve error handling for PyFloat_AsDouble/PyLong_AsLong/PyLong_AsUnsignedLong Check the value before to avoid calling PyErr_Occurred --- cairo/context.c | 2 +- cairo/enums.c | 2 +- cairo/glyph.c | 8 ++++---- cairo/misc.c | 2 +- 4 files changed, 7 insertions(+), 7 deletions(-) diff --git a/cairo/context.c b/cairo/context.c index 08ed5ca5..ebb9d728 100644 --- a/cairo/context.c +++ b/cairo/context.c @@ -901,7 +901,7 @@ pycairo_set_dash (PycairoContext *o, PyObject *args) { } dashes[i] = PyFloat_AsDouble(py_dash); Py_DECREF(py_dash); - if (PyErr_Occurred()) { + if (dashes[i] == -1.0 && PyErr_Occurred()) { PyMem_Free (dashes); Py_DECREF(py_dashes); return NULL; diff --git a/cairo/enums.c b/cairo/enums.c index d6037e4c..0871e3e7 100644 --- a/cairo/enums.c +++ b/cairo/enums.c @@ -267,7 +267,7 @@ format_stride_for_width (PyObject *self, PyObject *args) { return NULL; value = PyLong_AsLong (self); - if (PyErr_Occurred()) + if (value == -1 && PyErr_Occurred()) return NULL; if (value > INT_MAX || value < INT_MIN) { PyErr_SetString (PyExc_ValueError, "format value out of range"); diff --git a/cairo/glyph.c b/cairo/glyph.c index b2872c08..ce40e23e 100644 --- a/cairo/glyph.c +++ b/cairo/glyph.c @@ -94,7 +94,7 @@ _PycairoGlyphs_AsGlyphs (PyObject *py_object, int *num_glyphs) } x = PyFloat_AsDouble (py_x); Py_DECREF (py_x); - if (PyErr_Occurred()) { + if (x == 1.0 && PyErr_Occurred()) { goto error; } PyObject *py_y = PySequence_ITEM (py_seq, 2); @@ -103,7 +103,7 @@ _PycairoGlyphs_AsGlyphs (PyObject *py_object, int *num_glyphs) } y = PyFloat_AsDouble (py_y); Py_DECREF (py_y); - if (PyErr_Occurred()) { + if (y == 1.0 && PyErr_Occurred()) { goto error; } @@ -149,7 +149,7 @@ _PyGlyph_AsGlyph (PyObject *pyobj, cairo_glyph_t *glyph) { } x = PyFloat_AsDouble (py_x); Py_DECREF (py_x); - if (PyErr_Occurred()) { + if (x == 1.0 && PyErr_Occurred()) { return -1; } PyObject *py_y = PySequence_ITEM (pyobj, 2); @@ -158,7 +158,7 @@ _PyGlyph_AsGlyph (PyObject *pyobj, cairo_glyph_t *glyph) { } y = PyFloat_AsDouble (py_y); Py_DECREF (py_y); - if (PyErr_Occurred()) { + if (y == 1.0 && PyErr_Occurred()) { return -1; } diff --git a/cairo/misc.c b/cairo/misc.c index 63de289f..03cc51ed 100644 --- a/cairo/misc.c +++ b/cairo/misc.c @@ -199,7 +199,7 @@ _conv_pyobject_to_ulong (PyObject *pyobj, unsigned long *result) { return -1; temp = PyLong_AsUnsignedLong (pylong); - if (PyErr_Occurred ()) + if (temp == (unsigned long)-1 && PyErr_Occurred ()) return -1; *result = temp; From 25cdbbf0f5da66f947c6fa1dd478a78e6a13cb3d Mon Sep 17 00:00:00 2001 From: Christoph Reiter Date: Sun, 9 Nov 2025 08:39:02 +0100 Subject: [PATCH 13/13] Remove _conv_pyobject_to_ulong No longer needed with modern Python --- cairo/glyph.c | 3 ++- cairo/misc.c | 29 ----------------------------- cairo/private.h | 2 -- 3 files changed, 2 insertions(+), 32 deletions(-) diff --git a/cairo/glyph.c b/cairo/glyph.c index ce40e23e..ac4bdeb8 100644 --- a/cairo/glyph.c +++ b/cairo/glyph.c @@ -181,7 +181,8 @@ glyph_new (PyTypeObject *type, PyObject *args, PyObject *kwds) { KWDS, &pyindex, &x, &y)) return NULL; - if (_conv_pyobject_to_ulong (pyindex, &index) < 0) + index = PyLong_AsUnsignedLong (pyindex); + if (index == (unsigned long)-1 && PyErr_Occurred ()) return NULL; tuple_args = Py_BuildValue ("((kdd))", index, x, y); diff --git a/cairo/misc.c b/cairo/misc.c index 03cc51ed..237a2c92 100644 --- a/cairo/misc.c +++ b/cairo/misc.c @@ -176,32 +176,3 @@ Pycairo_richcompare (void* a, void *b, int op) Py_INCREF (res); return res; } - -/* NULL on error */ -static PyObject * -_conv_pyobject_to_pylong (PyObject *pyobj) { - if (!PyLong_Check (pyobj)) { - PyErr_SetString (PyExc_TypeError, "not of type int"); - return NULL; - } - Py_INCREF (pyobj); - return pyobj; -} - -/* -1 on error */ -int -_conv_pyobject_to_ulong (PyObject *pyobj, unsigned long *result) { - unsigned long temp; - PyObject *pylong; - - pylong = _conv_pyobject_to_pylong (pyobj); - if (pylong == NULL) - return -1; - - temp = PyLong_AsUnsignedLong (pylong); - if (temp == (unsigned long)-1 && PyErr_Occurred ()) - return -1; - - *result = temp; - return 0; -} diff --git a/cairo/private.h b/cairo/private.h index 6c2ce61d..2af9d0ec 100644 --- a/cairo/private.h +++ b/cairo/private.h @@ -59,8 +59,6 @@ int _PyGlyph_AsGlyph (PyObject *pyobj, cairo_glyph_t *glyph); int _PyTextCluster_AsTextCluster (PyObject *pyobj, cairo_text_cluster_t *cluster); -int _conv_pyobject_to_ulong (PyObject *pyobj, unsigned long *result); - PyObject* Pycairo_richcompare (void* a, void *b, int op); extern PyTypeObject PycairoContext_Type;