From 00b58ec92beb23c9805c3db314282644c2392873 Mon Sep 17 00:00:00 2001 From: Elliott Sales de Andrade Date: Fri, 11 Jul 2025 02:53:58 -0400 Subject: [PATCH 1/6] BLD: Bump pybind11 minimum to 3.0.0 --- meson.build | 2 +- pyproject.toml | 4 ++-- requirements/dev/build-requirements.txt | 2 +- 3 files changed, 4 insertions(+), 4 deletions(-) diff --git a/meson.build b/meson.build index 54249473fe8e..49415fb5a397 100644 --- a/meson.build +++ b/meson.build @@ -39,7 +39,7 @@ py_mod = import('python') py3 = py_mod.find_installation(pure: false) py3_dep = py3.dependency() -pybind11_dep = dependency('pybind11', version: '>=2.13.2') +pybind11_dep = dependency('pybind11', version: '>=3') subdir('extern') subdir('src') diff --git a/pyproject.toml b/pyproject.toml index cf8503a0f3fe..3b4f01b91214 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -47,7 +47,7 @@ requires-python = ">=3.11" # Should be a copy of the build dependencies below. dev = [ "meson-python>=0.13.1,!=0.17.*", - "pybind11>=2.13.2,!=2.13.3", + "pybind11>=3", "setuptools_scm>=7", # Not required by us but setuptools_scm without a version, cso _if_ # installed, then setuptools_scm 8 requires at least this version. @@ -73,7 +73,7 @@ requires = [ # meson-python 0.17.x breaks symlinks in sdists. You can remove this pin if # you really need it and aren't using an sdist. "meson-python>=0.13.1,!=0.17.*", - "pybind11>=2.13.2,!=2.13.3", + "pybind11>=3", "setuptools_scm>=7", ] diff --git a/requirements/dev/build-requirements.txt b/requirements/dev/build-requirements.txt index 4d2a098c3c4f..7b1c77f6fdd3 100644 --- a/requirements/dev/build-requirements.txt +++ b/requirements/dev/build-requirements.txt @@ -1,3 +1,3 @@ -pybind11>=2.13.2,!=2.13.3 +pybind11>=3 meson-python setuptools-scm From cdc717302f931f5643172a15415c1435647bdd09 Mon Sep 17 00:00:00 2001 From: Elliott Sales de Andrade Date: Fri, 11 Jul 2025 03:24:08 -0400 Subject: [PATCH 2/6] Remove workaround for unqualified Buffer in pybind11 --- src/ft2font_wrapper.cpp | 22 ++++++---------------- 1 file changed, 6 insertions(+), 16 deletions(-) diff --git a/src/ft2font_wrapper.cpp b/src/ft2font_wrapper.cpp index ca2db6aa0e5b..268235597c37 100644 --- a/src/ft2font_wrapper.cpp +++ b/src/ft2font_wrapper.cpp @@ -1605,8 +1605,8 @@ PYBIND11_MODULE(ft2font, m, py::mod_gil_not_used()) .def_property_readonly("bbox", &PyGlyph_get_bbox, "The control box of the glyph."); - auto cls = py::class_(m, "FT2Font", py::is_final(), py::buffer_protocol(), - PyFT2Font__doc__) + py::class_(m, "FT2Font", py::is_final(), py::buffer_protocol(), + PyFT2Font__doc__) .def(py::init(&PyFT2Font_init), "filename"_a, "hinting_factor"_a=8, py::kw_only(), "_fallback_list"_a=py::none(), "_kerning_factor"_a=0, @@ -1640,20 +1640,10 @@ PYBIND11_MODULE(ft2font, m, py::mod_gil_not_used()) .def("get_descent", &PyFT2Font_get_descent, PyFT2Font_get_descent__doc__) .def("draw_glyphs_to_bitmap", &PyFT2Font_draw_glyphs_to_bitmap, py::kw_only(), "antialiased"_a=true, - PyFT2Font_draw_glyphs_to_bitmap__doc__); - // The generated docstring uses an unqualified "Buffer" as type hint, - // which causes an error in sphinx. This is fixed as of pybind11 - // master (since #5566) which now uses "collections.abc.Buffer"; - // restore the signature once that version is released. - { - py::options options{}; - options.disable_function_signatures(); - cls - .def("draw_glyph_to_bitmap", &PyFT2Font_draw_glyph_to_bitmap, - "image"_a, "x"_a, "y"_a, "glyph"_a, py::kw_only(), "antialiased"_a=true, - PyFT2Font_draw_glyph_to_bitmap__doc__); - } - cls + PyFT2Font_draw_glyphs_to_bitmap__doc__) + .def("draw_glyph_to_bitmap", &PyFT2Font_draw_glyph_to_bitmap, + "image"_a, "x"_a, "y"_a, "glyph"_a, py::kw_only(), "antialiased"_a=true, + PyFT2Font_draw_glyph_to_bitmap__doc__) .def("get_glyph_name", &PyFT2Font_get_glyph_name, "index"_a, PyFT2Font_get_glyph_name__doc__) .def("get_charmap", &PyFT2Font_get_charmap, PyFT2Font_get_charmap__doc__) From 2774815cb3f1db492dc2beca1e2a00c496b84155 Mon Sep 17 00:00:00 2001 From: Elliott Sales de Andrade Date: Fri, 11 Jul 2025 04:54:34 -0400 Subject: [PATCH 3/6] Convert to py::native_enum And drop the manual wrapper to emulate it. --- src/_enums.h | 95 ---------------------------- src/_image_wrapper.cpp | 6 +- src/ft2font_wrapper.cpp | 136 +++++++++++++++++++--------------------- 3 files changed, 67 insertions(+), 170 deletions(-) delete mode 100644 src/_enums.h diff --git a/src/_enums.h b/src/_enums.h deleted file mode 100644 index 18f3d9aac9fa..000000000000 --- a/src/_enums.h +++ /dev/null @@ -1,95 +0,0 @@ -#ifndef MPL_ENUMS_H -#define MPL_ENUMS_H - -#include - -// Extension for pybind11: Pythonic enums. -// This allows creating classes based on ``enum.*`` types. -// This code was copied from mplcairo, with some slight tweaks. -// The API is: -// -// - P11X_DECLARE_ENUM(py_name: str, py_base_cls: str, ...: {str, enum value}): -// py_name: The name to expose in the module. -// py_base_cls: The name of the enum base class to use. -// ...: The enum name/value pairs to expose. -// -// Use this macro to declare an enum and its values. -// -// - py11x::bind_enums(m: pybind11::module): -// m: The module to use to register the enum classes. -// -// Place this in PYBIND11_MODULE to register the enums declared by P11X_DECLARE_ENUM. - -// a1 includes the opening brace and a2 the closing brace. -// This definition is compatible with older compiler versions compared to -// #define P11X_ENUM_TYPE(...) decltype(std::map{std::pair __VA_ARGS__})::mapped_type -#define P11X_ENUM_TYPE(a1, a2, ...) decltype(std::pair a1, a2)::second_type - -#define P11X_CAT2(a, b) a##b -#define P11X_CAT(a, b) P11X_CAT2(a, b) - -namespace p11x { - namespace { - namespace py = pybind11; - - // Holder is (py_base_cls, [(name, value), ...]) before module init; - // converted to the Python class object after init. - auto enums = std::unordered_map{}; - - auto bind_enums(py::module mod) -> void - { - for (auto& [py_name, spec]: enums) { - auto const& [py_base_cls, pairs] = - spec.cast>(); - mod.attr(py::cast(py_name)) = spec = - py::module::import("enum").attr(py_base_cls.c_str())( - py_name, pairs, py::arg("module") = mod.attr("__name__")); - } - } - } -} - -// Immediately converting the args to a vector outside of the lambda avoids -// name collisions. -#define P11X_DECLARE_ENUM(py_name, py_base_cls, ...) \ - namespace p11x { \ - namespace { \ - [[maybe_unused]] auto const P11X_CAT(enum_placeholder_, __COUNTER__) = \ - [](auto args) { \ - py::gil_scoped_acquire gil; \ - using int_t = std::underlying_type_t; \ - auto pairs = std::vector>{}; \ - for (auto& [k, v]: args) { \ - pairs.emplace_back(k, int_t(v)); \ - } \ - p11x::enums[py_name] = pybind11::cast(std::pair{py_base_cls, pairs}); \ - return 0; \ - } (std::vector{std::pair __VA_ARGS__}); \ - } \ - } \ - namespace pybind11::detail { \ - template<> struct type_caster { \ - using type = P11X_ENUM_TYPE(__VA_ARGS__); \ - static_assert(std::is_enum_v, "Not an enum"); \ - PYBIND11_TYPE_CASTER(type, _(py_name)); \ - bool load(handle src, bool) { \ - auto cls = p11x::enums.at(py_name); \ - PyObject* tmp = nullptr; \ - if (pybind11::isinstance(src, cls) \ - && (tmp = PyNumber_Index(src.attr("value").ptr()))) { \ - auto ival = PyLong_AsLong(tmp); \ - value = decltype(value)(ival); \ - Py_DECREF(tmp); \ - return !(ival == -1 && !PyErr_Occurred()); \ - } else { \ - return false; \ - } \ - } \ - static handle cast(decltype(value) obj, return_value_policy, handle) { \ - auto cls = p11x::enums.at(py_name); \ - return cls(std::underlying_type_t(obj)).inc_ref(); \ - } \ - }; \ - } - -#endif /* MPL_ENUMS_H */ diff --git a/src/_image_wrapper.cpp b/src/_image_wrapper.cpp index 6528c4a9270c..07de3462e412 100644 --- a/src/_image_wrapper.cpp +++ b/src/_image_wrapper.cpp @@ -1,4 +1,5 @@ #include +#include #include #include @@ -280,7 +281,7 @@ calculate_rms_and_diff(py::array_t expected_image, PYBIND11_MODULE(_image, m, py::mod_gil_not_used()) { - py::enum_(m, "_InterpolationType") + py::native_enum(m, "_InterpolationType", "enum.Enum") .value("NEAREST", NEAREST) .value("BILINEAR", BILINEAR) .value("BICUBIC", BICUBIC) @@ -298,7 +299,8 @@ PYBIND11_MODULE(_image, m, py::mod_gil_not_used()) .value("SINC", SINC) .value("LANCZOS", LANCZOS) .value("BLACKMAN", BLACKMAN) - .export_values(); + .export_values() + .finalize(); m.def("resample", &image_resample, "input_array"_a, diff --git a/src/ft2font_wrapper.cpp b/src/ft2font_wrapper.cpp index 268235597c37..32998f4cdfc8 100644 --- a/src/ft2font_wrapper.cpp +++ b/src/ft2font_wrapper.cpp @@ -1,10 +1,10 @@ #define NPY_NO_DEPRECATED_API NPY_1_7_API_VERSION #include +#include #include #include #include "ft2font.h" -#include "_enums.h" #include #include @@ -50,13 +50,6 @@ const char *Kerning__doc__ = R"""( .. versionadded:: 3.10 )"""; -P11X_DECLARE_ENUM( - "Kerning", "Enum", - {"DEFAULT", FT_KERNING_DEFAULT}, - {"UNFITTED", FT_KERNING_UNFITTED}, - {"UNSCALED", FT_KERNING_UNSCALED}, -); - const char *FaceFlags__doc__ = R"""( Flags returned by `FT2Font.face_flags`. @@ -103,29 +96,6 @@ enum class FaceFlags : FT_Long { #undef DECLARE_FLAG }; -P11X_DECLARE_ENUM( - "FaceFlags", "Flag", - {"SCALABLE", FaceFlags::SCALABLE}, - {"FIXED_SIZES", FaceFlags::FIXED_SIZES}, - {"FIXED_WIDTH", FaceFlags::FIXED_WIDTH}, - {"SFNT", FaceFlags::SFNT}, - {"HORIZONTAL", FaceFlags::HORIZONTAL}, - {"VERTICAL", FaceFlags::VERTICAL}, - {"KERNING", FaceFlags::KERNING}, - {"FAST_GLYPHS", FaceFlags::FAST_GLYPHS}, - {"MULTIPLE_MASTERS", FaceFlags::MULTIPLE_MASTERS}, - {"GLYPH_NAMES", FaceFlags::GLYPH_NAMES}, - {"EXTERNAL_STREAM", FaceFlags::EXTERNAL_STREAM}, - {"HINTER", FaceFlags::HINTER}, - {"CID_KEYED", FaceFlags::CID_KEYED}, - {"TRICKY", FaceFlags::TRICKY}, - {"COLOR", FaceFlags::COLOR}, - {"VARIATION", FaceFlags::VARIATION}, - {"SVG", FaceFlags::SVG}, - {"SBIX", FaceFlags::SBIX}, - {"SBIX_OVERLAY", FaceFlags::SBIX_OVERLAY}, -); - const char *LoadFlags__doc__ = R"""( Flags for `FT2Font.load_char`, `FT2Font.load_glyph`, and `FT2Font.set_text`. @@ -174,36 +144,6 @@ enum class LoadFlags : FT_Int32 { #undef DECLARE_FLAG }; -P11X_DECLARE_ENUM( - "LoadFlags", "Flag", - {"DEFAULT", LoadFlags::DEFAULT}, - {"NO_SCALE", LoadFlags::NO_SCALE}, - {"NO_HINTING", LoadFlags::NO_HINTING}, - {"RENDER", LoadFlags::RENDER}, - {"NO_BITMAP", LoadFlags::NO_BITMAP}, - {"VERTICAL_LAYOUT", LoadFlags::VERTICAL_LAYOUT}, - {"FORCE_AUTOHINT", LoadFlags::FORCE_AUTOHINT}, - {"CROP_BITMAP", LoadFlags::CROP_BITMAP}, - {"PEDANTIC", LoadFlags::PEDANTIC}, - {"IGNORE_GLOBAL_ADVANCE_WIDTH", LoadFlags::IGNORE_GLOBAL_ADVANCE_WIDTH}, - {"NO_RECURSE", LoadFlags::NO_RECURSE}, - {"IGNORE_TRANSFORM", LoadFlags::IGNORE_TRANSFORM}, - {"MONOCHROME", LoadFlags::MONOCHROME}, - {"LINEAR_DESIGN", LoadFlags::LINEAR_DESIGN}, - {"NO_AUTOHINT", LoadFlags::NO_AUTOHINT}, - {"COLOR", LoadFlags::COLOR}, - {"COMPUTE_METRICS", LoadFlags::COMPUTE_METRICS}, - {"BITMAP_METRICS_ONLY", LoadFlags::BITMAP_METRICS_ONLY}, - {"NO_SVG", LoadFlags::NO_SVG}, - // These must be unique, but the others can be OR'd together; I don't know if - // there's any way to really enforce that. - {"TARGET_NORMAL", LoadFlags::TARGET_NORMAL}, - {"TARGET_LIGHT", LoadFlags::TARGET_LIGHT}, - {"TARGET_MONO", LoadFlags::TARGET_MONO}, - {"TARGET_LCD", LoadFlags::TARGET_LCD}, - {"TARGET_LCD_V", LoadFlags::TARGET_LCD_V}, -); - const char *StyleFlags__doc__ = R"""( Flags returned by `FT2Font.style_flags`. @@ -221,13 +161,6 @@ enum class StyleFlags : FT_Long { #undef DECLARE_FLAG }; -P11X_DECLARE_ENUM( - "StyleFlags", "Flag", - {"NORMAL", StyleFlags::NORMAL}, - {"ITALIC", StyleFlags::ITALIC}, - {"BOLD", StyleFlags::BOLD}, -); - /********************************************************************** * FT2Image * */ @@ -1552,11 +1485,68 @@ PYBIND11_MODULE(ft2font, m, py::mod_gil_not_used()) FT_Library_Version(_ft2Library, &major, &minor, &patch); snprintf(version_string, sizeof(version_string), "%d.%d.%d", major, minor, patch); - p11x::bind_enums(m); - p11x::enums["Kerning"].attr("__doc__") = Kerning__doc__; - p11x::enums["LoadFlags"].attr("__doc__") = LoadFlags__doc__; - p11x::enums["FaceFlags"].attr("__doc__") = FaceFlags__doc__; - p11x::enums["StyleFlags"].attr("__doc__") = StyleFlags__doc__; + py::native_enum(m, "Kerning", "enum.Enum", Kerning__doc__) + .value("DEFAULT", FT_KERNING_DEFAULT) + .value("UNFITTED", FT_KERNING_UNFITTED) + .value("UNSCALED", FT_KERNING_UNSCALED) + .finalize(); + + py::native_enum(m, "LoadFlags", "enum.Flag", LoadFlags__doc__) + .value("DEFAULT", LoadFlags::DEFAULT) + .value("NO_SCALE", LoadFlags::NO_SCALE) + .value("NO_HINTING", LoadFlags::NO_HINTING) + .value("RENDER", LoadFlags::RENDER) + .value("NO_BITMAP", LoadFlags::NO_BITMAP) + .value("VERTICAL_LAYOUT", LoadFlags::VERTICAL_LAYOUT) + .value("FORCE_AUTOHINT", LoadFlags::FORCE_AUTOHINT) + .value("CROP_BITMAP", LoadFlags::CROP_BITMAP) + .value("PEDANTIC", LoadFlags::PEDANTIC) + .value("IGNORE_GLOBAL_ADVANCE_WIDTH", LoadFlags::IGNORE_GLOBAL_ADVANCE_WIDTH) + .value("NO_RECURSE", LoadFlags::NO_RECURSE) + .value("IGNORE_TRANSFORM", LoadFlags::IGNORE_TRANSFORM) + .value("MONOCHROME", LoadFlags::MONOCHROME) + .value("LINEAR_DESIGN", LoadFlags::LINEAR_DESIGN) + .value("NO_AUTOHINT", LoadFlags::NO_AUTOHINT) + .value("COLOR", LoadFlags::COLOR) + .value("COMPUTE_METRICS", LoadFlags::COMPUTE_METRICS) + .value("BITMAP_METRICS_ONLY", LoadFlags::BITMAP_METRICS_ONLY) + .value("NO_SVG", LoadFlags::NO_SVG) + // These must be unique, but the others can be OR'd together; I don't know if + // there's any way to really enforce that. + .value("TARGET_NORMAL", LoadFlags::TARGET_NORMAL) + .value("TARGET_LIGHT", LoadFlags::TARGET_LIGHT) + .value("TARGET_MONO", LoadFlags::TARGET_MONO) + .value("TARGET_LCD", LoadFlags::TARGET_LCD) + .value("TARGET_LCD_V", LoadFlags::TARGET_LCD_V) + .finalize(); + + py::native_enum(m, "FaceFlags", "enum.Flag", FaceFlags__doc__) + .value("SCALABLE", FaceFlags::SCALABLE) + .value("FIXED_SIZES", FaceFlags::FIXED_SIZES) + .value("FIXED_WIDTH", FaceFlags::FIXED_WIDTH) + .value("SFNT", FaceFlags::SFNT) + .value("HORIZONTAL", FaceFlags::HORIZONTAL) + .value("VERTICAL", FaceFlags::VERTICAL) + .value("KERNING", FaceFlags::KERNING) + .value("FAST_GLYPHS", FaceFlags::FAST_GLYPHS) + .value("MULTIPLE_MASTERS", FaceFlags::MULTIPLE_MASTERS) + .value("GLYPH_NAMES", FaceFlags::GLYPH_NAMES) + .value("EXTERNAL_STREAM", FaceFlags::EXTERNAL_STREAM) + .value("HINTER", FaceFlags::HINTER) + .value("CID_KEYED", FaceFlags::CID_KEYED) + .value("TRICKY", FaceFlags::TRICKY) + .value("COLOR", FaceFlags::COLOR) + .value("VARIATION", FaceFlags::VARIATION) + .value("SVG", FaceFlags::SVG) + .value("SBIX", FaceFlags::SBIX) + .value("SBIX_OVERLAY", FaceFlags::SBIX_OVERLAY) + .finalize(); + + py::native_enum(m, "StyleFlags", "enum.Flag", StyleFlags__doc__) + .value("NORMAL", StyleFlags::NORMAL) + .value("ITALIC", StyleFlags::ITALIC) + .value("BOLD", StyleFlags::BOLD) + .finalize(); py::class_(m, "FT2Image", py::is_final(), py::buffer_protocol(), PyFT2Image__doc__) From ae5b8f9bf105d2def3d3b3c2d4a5db279b01f693 Mon Sep 17 00:00:00 2001 From: Elliott Sales de Andrade Date: Fri, 11 Jul 2025 23:30:16 -0400 Subject: [PATCH 4/6] Switch pybind11 classes to smart holders This is the recommended, but not-yet-default, holder for C++ classes. --- src/_backend_agg_wrapper.cpp | 4 ++-- src/ft2font_wrapper.cpp | 6 +++--- src/tri/_tri_wrapper.cpp | 6 +++--- 3 files changed, 8 insertions(+), 8 deletions(-) diff --git a/src/_backend_agg_wrapper.cpp b/src/_backend_agg_wrapper.cpp index 3dd50b31f64a..6eb25a12e807 100644 --- a/src/_backend_agg_wrapper.cpp +++ b/src/_backend_agg_wrapper.cpp @@ -216,7 +216,7 @@ PyRendererAgg_draw_gouraud_triangles(RendererAgg *self, PYBIND11_MODULE(_backend_agg, m, py::mod_gil_not_used()) { - py::class_(m, "RendererAgg", py::buffer_protocol()) + py::classh(m, "RendererAgg", py::buffer_protocol()) .def(py::init(), "width"_a, "height"_a, "dpi"_a) @@ -266,7 +266,7 @@ PYBIND11_MODULE(_backend_agg, m, py::mod_gil_not_used()) return py::buffer_info(renderer->pixBuffer, shape, strides); }); - py::class_(m, "BufferRegion", py::buffer_protocol()) + py::classh(m, "BufferRegion", py::buffer_protocol()) // BufferRegion is not constructible from Python, thus no py::init is added. .def("set_x", &PyBufferRegion_set_x) .def("set_y", &PyBufferRegion_set_y) diff --git a/src/ft2font_wrapper.cpp b/src/ft2font_wrapper.cpp index 32998f4cdfc8..a4bf64445749 100644 --- a/src/ft2font_wrapper.cpp +++ b/src/ft2font_wrapper.cpp @@ -1548,7 +1548,7 @@ PYBIND11_MODULE(ft2font, m, py::mod_gil_not_used()) .value("BOLD", StyleFlags::BOLD) .finalize(); - py::class_(m, "FT2Image", py::is_final(), py::buffer_protocol(), + py::classh(m, "FT2Image", py::is_final(), py::buffer_protocol(), PyFT2Image__doc__) .def(py::init( [](double_or_ width, double_or_ height) { @@ -1571,7 +1571,7 @@ PYBIND11_MODULE(ft2font, m, py::mod_gil_not_used()) return py::buffer_info(self.get_buffer(), shape, strides); }); - py::class_(m, "Glyph", py::is_final(), PyGlyph__doc__) + py::classh(m, "Glyph", py::is_final(), PyGlyph__doc__) .def(py::init<>([]() -> PyGlyph { // Glyph is not useful from Python, so mark it as not constructible. throw std::runtime_error("Glyph is not constructible"); @@ -1595,7 +1595,7 @@ PYBIND11_MODULE(ft2font, m, py::mod_gil_not_used()) .def_property_readonly("bbox", &PyGlyph_get_bbox, "The control box of the glyph."); - py::class_(m, "FT2Font", py::is_final(), py::buffer_protocol(), + py::classh(m, "FT2Font", py::is_final(), py::buffer_protocol(), PyFT2Font__doc__) .def(py::init(&PyFT2Font_init), "filename"_a, "hinting_factor"_a=8, py::kw_only(), diff --git a/src/tri/_tri_wrapper.cpp b/src/tri/_tri_wrapper.cpp index 732e1af5f310..304b387b419e 100644 --- a/src/tri/_tri_wrapper.cpp +++ b/src/tri/_tri_wrapper.cpp @@ -4,7 +4,7 @@ using namespace pybind11::literals; PYBIND11_MODULE(_tri, m, py::mod_gil_not_used()) { - py::class_(m, "Triangulation", py::is_final()) + py::classh(m, "Triangulation", py::is_final()) .def(py::init(m, "TriContourGenerator", py::is_final()) + py::classh(m, "TriContourGenerator", py::is_final()) .def(py::init(), "triangulation"_a, @@ -44,7 +44,7 @@ PYBIND11_MODULE(_tri, m, py::mod_gil_not_used()) .def("create_filled_contour", &TriContourGenerator::create_filled_contour, "Create and return a filled contour."); - py::class_(m, "TrapezoidMapTriFinder", py::is_final()) + py::classh(m, "TrapezoidMapTriFinder", py::is_final()) .def(py::init(), "triangulation"_a, "Create a new C++ TrapezoidMapTriFinder object.\n" From 629be3fcce70e8124213a4db69af31e7ef84fa0f Mon Sep 17 00:00:00 2001 From: Elliott Sales de Andrade Date: Thu, 21 Aug 2025 03:07:45 -0400 Subject: [PATCH 5/6] Make FreeType library tied to the module This ensures that a new one will be created for each sub-interpreter, or if the module is reloaded. --- src/ft2font.cpp | 6 ++---- src/ft2font.h | 4 +--- src/ft2font_wrapper.cpp | 33 +++++++++++++++++++++++++++------ 3 files changed, 30 insertions(+), 13 deletions(-) diff --git a/src/ft2font.cpp b/src/ft2font.cpp index da1bd19dca57..b4b1e8f5231b 100644 --- a/src/ft2font.cpp +++ b/src/ft2font.cpp @@ -41,8 +41,6 @@ you have disabled hints). */ -FT_Library _ft2Library; - FT2Image::FT2Image(unsigned long width, unsigned long height) : m_buffer((unsigned char *)calloc(width * height, 1)), m_width(width), m_height(height) { @@ -207,7 +205,7 @@ FT2Font::get_path(std::vector &vertices, std::vector &cod codes.push_back(CLOSEPOLY); } -FT2Font::FT2Font(FT_Open_Args &open_args, +FT2Font::FT2Font(FT_Library ft2Library, FT_Open_Args &open_args, long hinting_factor_, std::vector &fallback_list, FT2Font::WarnFunc warn, bool warn_if_used) @@ -217,7 +215,7 @@ FT2Font::FT2Font(FT_Open_Args &open_args, kerning_factor(0) { clear(); - FT_CHECK(FT_Open_Face, _ft2Library, &open_args, 0, &face); + FT_CHECK(FT_Open_Face, ft2Library, &open_args, 0, &face); if (open_args.stream != nullptr) { face->face_flags |= FT_FACE_FLAG_EXTERNAL_STREAM; } diff --git a/src/ft2font.h b/src/ft2font.h index 6676a7dd4818..6402b82f2f68 100644 --- a/src/ft2font.h +++ b/src/ft2font.h @@ -92,14 +92,12 @@ class FT2Image FT2Image &operator=(const FT2Image &); }; -extern FT_Library _ft2Library; - class FT2Font { typedef void (*WarnFunc)(FT_ULong charcode, std::set family_names); public: - FT2Font(FT_Open_Args &open_args, long hinting_factor, + FT2Font(FT_Library ft2Library, FT_Open_Args &open_args, long hinting_factor, std::vector &fallback_list, WarnFunc warn, bool warn_if_used); virtual ~FT2Font(); diff --git a/src/ft2font_wrapper.cpp b/src/ft2font_wrapper.cpp index a4bf64445749..de2b96e048cc 100644 --- a/src/ft2font_wrapper.cpp +++ b/src/ft2font_wrapper.cpp @@ -379,7 +379,7 @@ const char *PyFT2Font_init__doc__ = R"""( )"""; static PyFT2Font * -PyFT2Font_init(py::object filename, long hinting_factor = 8, +PyFT2Font_init(FT_Library ft2Library, py::object filename, long hinting_factor = 8, std::optional> fallback_list = std::nullopt, int kerning_factor = 0, bool warn_if_used = false) { @@ -430,8 +430,8 @@ PyFT2Font_init(py::object filename, long hinting_factor = 8, self->stream.close = nullptr; } - self->x = new FT2Font(open_args, hinting_factor, fallback_fonts, ft_glyph_warn, - warn_if_used); + self->x = new FT2Font(ft2Library, open_args, hinting_factor, fallback_fonts, + ft_glyph_warn, warn_if_used); self->x->set_kerning_factor(kerning_factor); @@ -1477,12 +1477,14 @@ ft2font__getattr__(std::string name) { PYBIND11_MODULE(ft2font, m, py::mod_gil_not_used()) { - if (FT_Init_FreeType(&_ft2Library)) { // initialize library + FT_Library ft2Library = nullptr; + + if (FT_Init_FreeType(&ft2Library)) { // initialize library throw std::runtime_error("Could not initialize the freetype2 library"); } FT_Int major, minor, patch; char version_string[64]; - FT_Library_Version(_ft2Library, &major, &minor, &patch); + FT_Library_Version(ft2Library, &major, &minor, &patch); snprintf(version_string, sizeof(version_string), "%d.%d.%d", major, minor, patch); py::native_enum(m, "Kerning", "enum.Enum", Kerning__doc__) @@ -1597,7 +1599,14 @@ PYBIND11_MODULE(ft2font, m, py::mod_gil_not_used()) py::classh(m, "FT2Font", py::is_final(), py::buffer_protocol(), PyFT2Font__doc__) - .def(py::init(&PyFT2Font_init), + .def(py::init( + [ft2Library](py::object filename, long hinting_factor = 8, + std::optional> fallback_list = std::nullopt, + int kerning_factor = 0, bool warn_if_used = false) -> PyFT2Font * + { + return PyFT2Font_init(ft2Library, filename, hinting_factor, + fallback_list, kerning_factor, warn_if_used); + }), "filename"_a, "hinting_factor"_a=8, py::kw_only(), "_fallback_list"_a=py::none(), "_kerning_factor"_a=0, "_warn_if_used"_a=false, @@ -1754,6 +1763,18 @@ PYBIND11_MODULE(ft2font, m, py::mod_gil_not_used()) return self.x->get_image().request(); }); + // Ensure FreeType library is closed after all instances of FT2Font are gone by + // tying a weak ref to the class itself. + (void)py::weakref( + m.attr("FT2Font"), + py::cpp_function( + [ft2Library](py::handle weakref) { + FT_Done_FreeType(ft2Library); + weakref.dec_ref(); + } + ) + ).release(); + m.attr("__freetype_version__") = version_string; m.attr("__freetype_build_type__") = FREETYPE_BUILD_TYPE; m.def("__getattr__", ft2font__getattr__); From 0d25798ecba3b962b0e7f27a4e6fcc0d9c9ec769 Mon Sep 17 00:00:00 2001 From: Elliott Sales de Andrade Date: Thu, 21 Aug 2025 04:02:13 -0400 Subject: [PATCH 6/6] Add support for Python subinterpreters Note, at this time, NumPy does not support subinterpreters, and so we do not either. --- src/_backend_agg_wrapper.cpp | 10 +++++++++- src/_image_wrapper.cpp | 9 ++++++++- src/_path_wrapper.cpp | 9 ++++++++- src/_qhull_wrapper.cpp | 9 ++++++++- src/ft2font_wrapper.cpp | 9 ++++++++- src/tri/_tri_wrapper.cpp | 9 ++++++++- 6 files changed, 49 insertions(+), 6 deletions(-) diff --git a/src/_backend_agg_wrapper.cpp b/src/_backend_agg_wrapper.cpp index 6eb25a12e807..5e2000c25178 100644 --- a/src/_backend_agg_wrapper.cpp +++ b/src/_backend_agg_wrapper.cpp @@ -1,6 +1,10 @@ #include #include #include +#ifdef PYBIND11_HAS_SUBINTERPRETER_SUPPORT +#include +#endif + #include "mplutils.h" #include "py_converters.h" #include "_backend_agg.h" @@ -214,7 +218,11 @@ PyRendererAgg_draw_gouraud_triangles(RendererAgg *self, self->draw_gouraud_triangles(gc, points, colors, trans); } -PYBIND11_MODULE(_backend_agg, m, py::mod_gil_not_used()) +PYBIND11_MODULE(_backend_agg, m, py::mod_gil_not_used() +#ifdef PYBIND11_HAS_SUBINTERPRETER_SUPPORT + ,py::multiple_interpreters::per_interpreter_gil() +#endif +) { py::classh(m, "RendererAgg", py::buffer_protocol()) .def(py::init(), diff --git a/src/_image_wrapper.cpp b/src/_image_wrapper.cpp index 07de3462e412..9346abb104d4 100644 --- a/src/_image_wrapper.cpp +++ b/src/_image_wrapper.cpp @@ -1,6 +1,9 @@ #include #include #include +#ifdef PYBIND11_HAS_SUBINTERPRETER_SUPPORT +#include +#endif #include @@ -279,7 +282,11 @@ calculate_rms_and_diff(py::array_t expected_image, } -PYBIND11_MODULE(_image, m, py::mod_gil_not_used()) +PYBIND11_MODULE(_image, m, py::mod_gil_not_used() +#ifdef PYBIND11_HAS_SUBINTERPRETER_SUPPORT + ,py::multiple_interpreters::per_interpreter_gil() +#endif +) { py::native_enum(m, "_InterpolationType", "enum.Enum") .value("NEAREST", NEAREST) diff --git a/src/_path_wrapper.cpp b/src/_path_wrapper.cpp index 2a297e49ac92..4b52cb3e2fe1 100644 --- a/src/_path_wrapper.cpp +++ b/src/_path_wrapper.cpp @@ -1,5 +1,8 @@ #include #include +#ifdef PYBIND11_HAS_SUBINTERPRETER_SUPPORT +#include +#endif #include #include @@ -310,7 +313,11 @@ Py_is_sorted_and_has_non_nan(py::object obj) return result; } -PYBIND11_MODULE(_path, m, py::mod_gil_not_used()) +PYBIND11_MODULE(_path, m, py::mod_gil_not_used() +#ifdef PYBIND11_HAS_SUBINTERPRETER_SUPPORT + ,py::multiple_interpreters::per_interpreter_gil() +#endif +) { m.def("point_in_path", &Py_point_in_path, "x"_a, "y"_a, "radius"_a, "path"_a, "trans"_a); diff --git a/src/_qhull_wrapper.cpp b/src/_qhull_wrapper.cpp index f8a3103b65f1..4f3ca648490a 100644 --- a/src/_qhull_wrapper.cpp +++ b/src/_qhull_wrapper.cpp @@ -7,6 +7,9 @@ */ #include #include +#ifdef PYBIND11_HAS_SUBINTERPRETER_SUPPORT +#include +#endif #ifdef _MSC_VER /* The Qhull header does not declare this as extern "C", but only MSVC seems to @@ -276,7 +279,11 @@ delaunay(const CoordArray& x, const CoordArray& y, int verbose) return delaunay_impl(npoints, x.data(), y.data(), verbose == 0); } -PYBIND11_MODULE(_qhull, m, py::mod_gil_not_used()) +PYBIND11_MODULE(_qhull, m, py::mod_gil_not_used() +#ifdef PYBIND11_HAS_SUBINTERPRETER_SUPPORT + ,py::multiple_interpreters::per_interpreter_gil() +#endif +) { m.doc() = "Computing Delaunay triangulations.\n"; diff --git a/src/ft2font_wrapper.cpp b/src/ft2font_wrapper.cpp index de2b96e048cc..99e41bb63be7 100644 --- a/src/ft2font_wrapper.cpp +++ b/src/ft2font_wrapper.cpp @@ -3,6 +3,9 @@ #include #include #include +#ifdef PYBIND11_HAS_SUBINTERPRETER_SUPPORT +#include +#endif #include "ft2font.h" @@ -1475,7 +1478,11 @@ ft2font__getattr__(std::string name) { "module 'matplotlib.ft2font' has no attribute {!r}"_s.format(name)); } -PYBIND11_MODULE(ft2font, m, py::mod_gil_not_used()) +PYBIND11_MODULE(ft2font, m, py::mod_gil_not_used() +#ifdef PYBIND11_HAS_SUBINTERPRETER_SUPPORT + ,py::multiple_interpreters::per_interpreter_gil() +#endif +) { FT_Library ft2Library = nullptr; diff --git a/src/tri/_tri_wrapper.cpp b/src/tri/_tri_wrapper.cpp index 304b387b419e..d3bf30e4153c 100644 --- a/src/tri/_tri_wrapper.cpp +++ b/src/tri/_tri_wrapper.cpp @@ -1,8 +1,15 @@ #include "_tri.h" +#ifdef PYBIND11_HAS_SUBINTERPRETER_SUPPORT +#include +#endif using namespace pybind11::literals; -PYBIND11_MODULE(_tri, m, py::mod_gil_not_used()) +PYBIND11_MODULE(_tri, m, py::mod_gil_not_used() +#ifdef PYBIND11_HAS_SUBINTERPRETER_SUPPORT + ,py::multiple_interpreters::per_interpreter_gil() +#endif +) { py::classh(m, "Triangulation", py::is_final()) .def(py::init pFad - Phonifier reborn

Pfad - The Proxy pFad of © 2024 Garber Painting. All rights reserved.

Note: This service is not intended for secure transactions such as banking, social media, email, or purchasing. Use at your own risk. We assume no liability whatsoever for broken pages.


Alternative Proxies:

Alternative Proxy

pFad Proxy

pFad v3 Proxy

pFad v4 Proxy