From 14f1bbe6b884d9363378f42898d198a99a584e48 Mon Sep 17 00:00:00 2001 From: Eisuke Kawashima Date: Sun, 3 May 2026 15:55:57 +0900 Subject: [PATCH 01/14] refactor: use PyUnicode_AsUTF8 with Python 3.14+ _PyUnicode_AsString is deprecated since Python 3.14 https://docs.python.org/3.14/deprecations/index.html#id2 fix #512 --- src/converter/builtin_converters.cpp | 14 +++++--------- 1 file changed, 5 insertions(+), 9 deletions(-) diff --git a/src/converter/builtin_converters.cpp b/src/converter/builtin_converters.cpp index ee2d5b4794..227fe2f805 100644 --- a/src/converter/builtin_converters.cpp +++ b/src/converter/builtin_converters.cpp @@ -40,22 +40,18 @@ namespace // An lvalue conversion function which extracts a char const* from a // Python String. -#if PY_VERSION_HEX < 0x03000000 void* convert_to_cstring(PyObject* obj) { +#if PY_VERSION_HEX < 0x03000000 return PyString_Check(obj) ? PyString_AsString(obj) : 0; - } #elif PY_VERSION_HEX < 0x03070000 - void* convert_to_cstring(PyObject* obj) - { return PyUnicode_Check(obj) ? _PyUnicode_AsString(obj) : 0; - } -#else - void* convert_to_cstring(PyObject* obj) - { +#elif PY_VERSION_HEX < 0x030E0000 return PyUnicode_Check(obj) ? const_cast(reinterpret_cast(_PyUnicode_AsString(obj))) : 0; - } +#else + return PyUnicode_Check(obj) ? const_cast(reinterpret_cast(PyUnicode_AsUTF8(obj))) : 0; #endif + } // Given a target type and a SlotPolicy describing how to perform a // given conversion, registers from_python converters which use the From 9e2f5191e53f12248175a434f7f7496454e09fa4 Mon Sep 17 00:00:00 2001 From: O01eg <397177+o01eg@users.noreply.github.com> Date: Sun, 20 Apr 2025 09:24:16 +0400 Subject: [PATCH 02/14] Add support for multi-phase module initialization --- include/boost/python/module.hpp | 3 ++ include/boost/python/module_init.hpp | 54 ++++++++++++++++++++++++++++ src/module.cpp | 12 +++++++ test/fabscript | 5 +++ test/module_multi_phase.cpp | 15 ++++++++ test/module_multi_phase.py | 23 ++++++++++++ 6 files changed, 112 insertions(+) create mode 100644 test/module_multi_phase.cpp create mode 100644 test/module_multi_phase.py diff --git a/include/boost/python/module.hpp b/include/boost/python/module.hpp index 8ad69f5a34..f6f9fa871f 100644 --- a/include/boost/python/module.hpp +++ b/include/boost/python/module.hpp @@ -9,5 +9,8 @@ # include # define BOOST_PYTHON_MODULE BOOST_PYTHON_MODULE_INIT +# if PY_VERSION_HEX >= 0x03050000 +# define BOOST_PYTHON_MODULE_MULTI_PHASE BOOST_PYTHON_MODULE_MULTI_PHASE_INIT +# endif #endif // MODULE_DWA20011221_HPP diff --git a/include/boost/python/module_init.hpp b/include/boost/python/module_init.hpp index 390db82cf4..f12550b221 100644 --- a/include/boost/python/module_init.hpp +++ b/include/boost/python/module_init.hpp @@ -47,6 +47,12 @@ namespace detail { BOOST_PYTHON_DECL PyObject* init_module(PyModuleDef&, void(*)(), bool gil_not_used = false); +# if PY_VERSION_HEX >= 0x03050000 + +BOOST_PYTHON_DECL int exec_module(PyObject*, void(*)()); + +# endif + #else BOOST_PYTHON_DECL PyObject* init_module(char const* name, void(*)()); @@ -115,6 +121,46 @@ BOOST_PYTHON_DECL PyObject* init_module(char const* name, void(*)()); void BOOST_PP_CAT(init_module_, name)() # endif // HAS_CXX11 +# if PY_VERSION_HEX >= 0x03050000 + +# define _BOOST_PYTHON_MODULE_MULTI_PHASE_INIT(name) \ + int BOOST_PP_CAT(exec_module_,name)(PyObject* module) \ + { \ + return boost::python::detail::exec_module( \ + module, BOOST_PP_CAT(init_module_, name) ); \ + } \ + extern "C" BOOST_SYMBOL_EXPORT PyObject* BOOST_PP_CAT(PyInit_, name)() \ + { \ + static PyModuleDef_Base initial_m_base = { \ + PyObject_HEAD_INIT(NULL) \ + 0, /* m_init */ \ + 0, /* m_index */ \ + 0 /* m_copy */ }; \ + static PyMethodDef initial_methods[] = { { 0, 0, 0, 0 } }; \ + \ + static PyModuleDef_Slot slots[] = { \ + {Py_mod_exec, reinterpret_cast(reinterpret_cast(BOOST_PP_CAT(exec_module_, name)))}, \ + {0, NULL} \ + }; \ + \ + static struct PyModuleDef moduledef = { \ + initial_m_base, \ + BOOST_PP_STRINGIZE(name), \ + 0, /* m_doc */ \ + 0, /* m_size */ \ + initial_methods, \ + slots, /* m_slots */ \ + 0, /* m_traverse */ \ + 0, /* m_clear */ \ + 0, /* m_free */ \ + }; \ + \ + return PyModuleDef_Init(&moduledef); \ + } \ + void BOOST_PP_CAT(init_module_, name)() + +# endif + # else # define _BOOST_PYTHON_MODULE_INIT(name) \ @@ -137,6 +183,14 @@ extern "C" BOOST_SYMBOL_EXPORT _BOOST_PYTHON_MODULE_INIT(name, __VA_ARGS__) extern "C" BOOST_SYMBOL_EXPORT _BOOST_PYTHON_MODULE_INIT(name) # endif // HAS_CXX11 && Python 3 +# if PY_VERSION_HEX >= 0x03050000 + +# define BOOST_PYTHON_MODULE_MULTI_PHASE_INIT(name) \ + void BOOST_PP_CAT(init_module_,name)(); \ +extern "C" BOOST_SYMBOL_EXPORT _BOOST_PYTHON_MODULE_MULTI_PHASE_INIT(name) + +# endif + # endif #endif // MODULE_INIT_DWA20020722_HPP diff --git a/src/module.cpp b/src/module.cpp index c32f4187bc..ef83d31807 100644 --- a/src/module.cpp +++ b/src/module.cpp @@ -52,6 +52,18 @@ BOOST_PYTHON_DECL PyObject* init_module(PyModuleDef& moduledef, init_function); } +# if PY_VERSION_HEX >= 0x03050000 + +BOOST_PYTHON_DECL int exec_module(PyObject* module, void(*init_function)()) +{ + PyObject* retval = init_module_in_scope( + module, + init_function); + return retval ? 0 : -1; +} + +# endif + #else namespace diff --git a/test/fabscript b/test/fabscript index 7cf22f9c09..362ff82d18 100644 --- a/test/fabscript +++ b/test/fabscript @@ -174,4 +174,9 @@ for t in ['numpy/dtype', tests.append(extension_test(t, numpy=True, condition=set.define.contains('HAS_NUMPY'))) +python_version_major, python_version_minor = map(int, python.instance().version.split('.')[:2]) + +tests.append(extension_test("module_multi_phase", + condition=python_version_major > 3 or (python_version_major == 3 and python_version_minor >= 5))) + default = report('report', tests, fail_on_failures=True) diff --git a/test/module_multi_phase.cpp b/test/module_multi_phase.cpp new file mode 100644 index 0000000000..dcbbe74305 --- /dev/null +++ b/test/module_multi_phase.cpp @@ -0,0 +1,15 @@ +// Distributed under the Boost Software License, Version 1.0. (See +// accompanying file LICENSE_1_0.txt or copy at +// http://www.boost.org/LICENSE_1_0.txt) + +#include +#include + +using namespace boost::python; + +BOOST_PYTHON_MODULE_MULTI_PHASE(module_multi_phase_ext) +{ + scope().attr("x") = "x"; +} + +#include "module_tail.cpp" diff --git a/test/module_multi_phase.py b/test/module_multi_phase.py new file mode 100644 index 0000000000..138f5d9820 --- /dev/null +++ b/test/module_multi_phase.py @@ -0,0 +1,23 @@ +# Distributed under the Boost +# Software License, Version 1.0. (See accompanying +# file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) +""" +>>> import module_multi_phase_ext +>>> module_multi_phase_ext.x +'x' +""" + +def run(args = None): + import sys + import doctest + + if args is not None: + sys.argv = args + return doctest.testmod(sys.modules.get(__name__)) + +if __name__ == '__main__': + print("running...") + import sys + status = run()[0] + if (status == 0): print("Done.") + sys.exit(status) From 0796140961369513bdf230596861eec781867885 Mon Sep 17 00:00:00 2001 From: O01eg <397177+o01eg@users.noreply.github.com> Date: Sat, 4 Apr 2026 22:34:27 +0400 Subject: [PATCH 03/14] Fix indent and add more remarks to make it more manageable --- include/boost/python/module_init.hpp | 20 ++++++++++---------- 1 file changed, 10 insertions(+), 10 deletions(-) diff --git a/include/boost/python/module_init.hpp b/include/boost/python/module_init.hpp index f12550b221..ba62259bff 100644 --- a/include/boost/python/module_init.hpp +++ b/include/boost/python/module_init.hpp @@ -13,7 +13,7 @@ namespace boost { namespace python { -#ifdef HAS_CXX11 +# ifdef HAS_CXX11 // Use to activate the Py_MOD_GIL_NOT_USED flag. class mod_gil_not_used { public: @@ -39,7 +39,7 @@ inline bool gil_not_used_option(F &&, O &&...o) { } } -#endif // HAS_CXX11 +# endif // HAS_CXX11 namespace detail { @@ -51,13 +51,13 @@ BOOST_PYTHON_DECL PyObject* init_module(PyModuleDef&, void(*)(), bool gil_not_us BOOST_PYTHON_DECL int exec_module(PyObject*, void(*)()); -# endif +# endif // PY_VERSION_HEX >= 0x03050000 -#else +# else // PY_VERSION_HEX >= 0x03000000 BOOST_PYTHON_DECL PyObject* init_module(char const* name, void(*)()); -#endif +# endif // PY_VERSION_HEX >= 0x03000000 }}} @@ -159,9 +159,9 @@ BOOST_PYTHON_DECL PyObject* init_module(char const* name, void(*)()); } \ void BOOST_PP_CAT(init_module_, name)() -# endif +# endif // PY_VERSION_HEX >= 0x03050000 -# else +# else // ! PY_VERSION_HEX >= 0x03000000 # define _BOOST_PYTHON_MODULE_INIT(name) \ void BOOST_PP_CAT(init,name)() \ @@ -171,7 +171,7 @@ BOOST_PYTHON_DECL PyObject* init_module(char const* name, void(*)()); } \ void BOOST_PP_CAT(init_module_,name)() -# endif +# endif // PY_VERSION_HEX >= 0x03000000 # if defined(HAS_CXX11) && (PY_VERSION_HEX >= 0x03000000) # define BOOST_PYTHON_MODULE_INIT(name, ...) \ @@ -189,8 +189,8 @@ extern "C" BOOST_SYMBOL_EXPORT _BOOST_PYTHON_MODULE_INIT(name) void BOOST_PP_CAT(init_module_,name)(); \ extern "C" BOOST_SYMBOL_EXPORT _BOOST_PYTHON_MODULE_MULTI_PHASE_INIT(name) -# endif +# endif // PY_VERSION_HEX >= 0x03050000 -# endif +# endif // BOOST_PYTHON_MODULE_INIT #endif // MODULE_INIT_DWA20020722_HPP From b8b00933c6cecbb47e871855ebef431524fc9869 Mon Sep 17 00:00:00 2001 From: O01eg <397177+o01eg@users.noreply.github.com> Date: Sat, 4 Apr 2026 22:51:30 +0400 Subject: [PATCH 04/14] Add mod_gil_not_used option to BOOST_PYTHON_MODULE_MULTI_PHASE_INIT --- include/boost/python/module_init.hpp | 51 +++++++++++++++++++++++++--- 1 file changed, 47 insertions(+), 4 deletions(-) diff --git a/include/boost/python/module_init.hpp b/include/boost/python/module_init.hpp index ba62259bff..05ead85c71 100644 --- a/include/boost/python/module_init.hpp +++ b/include/boost/python/module_init.hpp @@ -123,7 +123,8 @@ BOOST_PYTHON_DECL PyObject* init_module(char const* name, void(*)()); # if PY_VERSION_HEX >= 0x03050000 -# define _BOOST_PYTHON_MODULE_MULTI_PHASE_INIT(name) \ +# if defined(HAS_CXX11) && (PY_VERSION_HEX >= 0x030D0000) +# define _BOOST_PYTHON_MODULE_MULTI_PHASE_INIT(name, ...) \ int BOOST_PP_CAT(exec_module_,name)(PyObject* module) \ { \ return boost::python::detail::exec_module( \ @@ -140,6 +141,7 @@ BOOST_PYTHON_DECL PyObject* init_module(char const* name, void(*)()); \ static PyModuleDef_Slot slots[] = { \ {Py_mod_exec, reinterpret_cast(reinterpret_cast(BOOST_PP_CAT(exec_module_, name)))}, \ + {Py_mod_gil, boost::python::detail::gil_not_used_option(__VA_ARGS__) ? Py_MOD_GIL_NOT_USED : Py_MOD_GIL_USED}, \ {0, NULL} \ }; \ \ @@ -158,6 +160,43 @@ BOOST_PYTHON_DECL PyObject* init_module(char const* name, void(*)()); return PyModuleDef_Init(&moduledef); \ } \ void BOOST_PP_CAT(init_module_, name)() +# else // ! HAS_CXX11 && Python 3.13+ +# define _BOOST_PYTHON_MODULE_MULTI_PHASE_INIT(name) \ + int BOOST_PP_CAT(exec_module_,name)(PyObject* module) \ + { \ + return boost::python::detail::exec_module( \ + module, BOOST_PP_CAT(init_module_, name) ); \ + } \ + extern "C" BOOST_SYMBOL_EXPORT PyObject* BOOST_PP_CAT(PyInit_, name)() \ + { \ + static PyModuleDef_Base initial_m_base = { \ + PyObject_HEAD_INIT(NULL) \ + 0, /* m_init */ \ + 0, /* m_index */ \ + 0 /* m_copy */ }; \ + static PyMethodDef initial_methods[] = { { 0, 0, 0, 0 } }; \ + \ + static PyModuleDef_Slot slots[] = { \ + {Py_mod_exec, reinterpret_cast(reinterpret_cast(BOOST_PP_CAT(exec_module_, name)))}, \ + {0, NULL} \ + }; \ + \ + static struct PyModuleDef moduledef = { \ + initial_m_base, \ + BOOST_PP_STRINGIZE(name), \ + 0, /* m_doc */ \ + 0, /* m_size */ \ + initial_methods, \ + slots, /* m_slots */ \ + 0, /* m_traverse */ \ + 0, /* m_clear */ \ + 0, /* m_free */ \ + }; \ + \ + return PyModuleDef_Init(&moduledef); \ + } \ + void BOOST_PP_CAT(init_module_, name)() +# endif // HAS_CXX11 && Python 3.13+ # endif // PY_VERSION_HEX >= 0x03050000 @@ -184,11 +223,15 @@ extern "C" BOOST_SYMBOL_EXPORT _BOOST_PYTHON_MODULE_INIT(name) # endif // HAS_CXX11 && Python 3 # if PY_VERSION_HEX >= 0x03050000 - -# define BOOST_PYTHON_MODULE_MULTI_PHASE_INIT(name) \ +# if defined(HAS_CXX11) && (PY_VERSION_HEX >= 0x030D0000) +# define BOOST_PYTHON_MODULE_MULTI_PHASE_INIT(name, ...) \ + void BOOST_PP_CAT(init_module_,name)(); \ +extern "C" BOOST_SYMBOL_EXPORT _BOOST_PYTHON_MODULE_MULTI_PHASE_INIT(name, __VA_ARGS__) +# else +# define BOOST_PYTHON_MODULE_MULTI_PHASE_INIT(name) \ void BOOST_PP_CAT(init_module_,name)(); \ extern "C" BOOST_SYMBOL_EXPORT _BOOST_PYTHON_MODULE_MULTI_PHASE_INIT(name) - +# endif // HAS_CXX11 && Python 3.13+ # endif // PY_VERSION_HEX >= 0x03050000 # endif // BOOST_PYTHON_MODULE_INIT From b34c8953a6543c50ad2ce5dd9e1cb01d3dea6c0c Mon Sep 17 00:00:00 2001 From: O01eg <397177+o01eg@users.noreply.github.com> Date: Sat, 4 Apr 2026 22:53:54 +0400 Subject: [PATCH 05/14] Rename module to mod --- include/boost/python/module_init.hpp | 8 ++++---- src/module.cpp | 4 ++-- 2 files changed, 6 insertions(+), 6 deletions(-) diff --git a/include/boost/python/module_init.hpp b/include/boost/python/module_init.hpp index 05ead85c71..0d826be654 100644 --- a/include/boost/python/module_init.hpp +++ b/include/boost/python/module_init.hpp @@ -125,10 +125,10 @@ BOOST_PYTHON_DECL PyObject* init_module(char const* name, void(*)()); # if defined(HAS_CXX11) && (PY_VERSION_HEX >= 0x030D0000) # define _BOOST_PYTHON_MODULE_MULTI_PHASE_INIT(name, ...) \ - int BOOST_PP_CAT(exec_module_,name)(PyObject* module) \ + int BOOST_PP_CAT(exec_module_,name)(PyObject* mod) \ { \ return boost::python::detail::exec_module( \ - module, BOOST_PP_CAT(init_module_, name) ); \ + mod, BOOST_PP_CAT(init_module_, name) ); \ } \ extern "C" BOOST_SYMBOL_EXPORT PyObject* BOOST_PP_CAT(PyInit_, name)() \ { \ @@ -162,10 +162,10 @@ BOOST_PYTHON_DECL PyObject* init_module(char const* name, void(*)()); void BOOST_PP_CAT(init_module_, name)() # else // ! HAS_CXX11 && Python 3.13+ # define _BOOST_PYTHON_MODULE_MULTI_PHASE_INIT(name) \ - int BOOST_PP_CAT(exec_module_,name)(PyObject* module) \ + int BOOST_PP_CAT(exec_module_,name)(PyObject* mod) \ { \ return boost::python::detail::exec_module( \ - module, BOOST_PP_CAT(init_module_, name) ); \ + mod, BOOST_PP_CAT(init_module_, name) ); \ } \ extern "C" BOOST_SYMBOL_EXPORT PyObject* BOOST_PP_CAT(PyInit_, name)() \ { \ diff --git a/src/module.cpp b/src/module.cpp index ef83d31807..22c4c5868a 100644 --- a/src/module.cpp +++ b/src/module.cpp @@ -54,10 +54,10 @@ BOOST_PYTHON_DECL PyObject* init_module(PyModuleDef& moduledef, # if PY_VERSION_HEX >= 0x03050000 -BOOST_PYTHON_DECL int exec_module(PyObject* module, void(*init_function)()) +BOOST_PYTHON_DECL int exec_module(PyObject* mod, void(*init_function)()) { PyObject* retval = init_module_in_scope( - module, + mod, init_function); return retval ? 0 : -1; } From 97a623d3ed1b62605d3b8d57d3b5e6bde237f0f1 Mon Sep 17 00:00:00 2001 From: O01eg <397177+o01eg@users.noreply.github.com> Date: Sat, 4 Apr 2026 23:12:47 +0400 Subject: [PATCH 06/14] Add tests --- test/fabscript | 2 ++ test/module_multi_phase_nogil.cpp | 25 +++++++++++++++++++++++++ test/module_multi_phase_nogil.py | 29 +++++++++++++++++++++++++++++ 3 files changed, 56 insertions(+) create mode 100644 test/module_multi_phase_nogil.cpp create mode 100644 test/module_multi_phase_nogil.py diff --git a/test/fabscript b/test/fabscript index 362ff82d18..f418ed3efb 100644 --- a/test/fabscript +++ b/test/fabscript @@ -178,5 +178,7 @@ python_version_major, python_version_minor = map(int, python.instance().version. tests.append(extension_test("module_multi_phase", condition=python_version_major > 3 or (python_version_major == 3 and python_version_minor >= 5))) +tests.append(extension_test("module_multi_phase_nogil", + condition=python_version_major > 3 or (python_version_major == 3 and python_version_minor >= 5))) default = report('report', tests, fail_on_failures=True) diff --git a/test/module_multi_phase_nogil.cpp b/test/module_multi_phase_nogil.cpp new file mode 100644 index 0000000000..6de74c29d7 --- /dev/null +++ b/test/module_multi_phase_nogil.cpp @@ -0,0 +1,25 @@ +// Test for BOOST_PYTHON_MODULE_MULTI_PHASE with optional mod_gil_not_used argument + +#include +#include + +// Simple function to export +int get_value() { + return 1234; +} + +#if defined(HAS_CXX11) && (PY_VERSION_HEX >= 0x030D0000) +// C++11 build with Python 3.13+: test with mod_gil_not_used option +BOOST_PYTHON_MODULE_MULTI_PHASE(module_multi_phase_nogil_ext, boost::python::mod_gil_not_used()) +{ + using namespace boost::python; + def("get_value", get_value); +} +#else +// C++98 build or Python 3.12-: test without optional arguments +BOOST_PYTHON_MODULE_MULTI_PHASE(module_multi_phase_nogil_ext) +{ + using namespace boost::python; + def("get_value", get_value); +} +#endif diff --git a/test/module_multi_phase_nogil.py b/test/module_multi_phase_nogil.py new file mode 100644 index 0000000000..30c6bcb984 --- /dev/null +++ b/test/module_multi_phase_nogil.py @@ -0,0 +1,29 @@ +""" +>>> from module_multi_phase_nogil_ext import * +>>> get_value() +1234 +>>> import sys, sysconfig +>>> Py_GIL_DISABLED = bool(sysconfig.get_config_var('Py_GIL_DISABLED')) +>>> if Py_GIL_DISABLED and sys._is_gil_enabled(): +... print('GIL is enabled and should not be') +... else: +... print('okay') +okay +""" + +from __future__ import print_function + +def run(args = None): + import sys + import doctest + + if args is not None: + sys.argv = args + return doctest.testmod(sys.modules.get(__name__)) + +if __name__ == '__main__': + print("running...") + import sys + status = run()[0] + if (status == 0): print("Done.") + sys.exit(status) From d018bb17d4083565a23b14ad254d1b92353b804c Mon Sep 17 00:00:00 2001 From: O01eg <397177+o01eg@users.noreply.github.com> Date: Fri, 26 Jun 2026 08:12:02 +0400 Subject: [PATCH 07/14] Add POD state for multi-phase modules --- include/boost/python/module.hpp | 1 + include/boost/python/module_init.hpp | 86 ++++++++++++++++++++++++++++ 2 files changed, 87 insertions(+) diff --git a/include/boost/python/module.hpp b/include/boost/python/module.hpp index f6f9fa871f..4ec34868b6 100644 --- a/include/boost/python/module.hpp +++ b/include/boost/python/module.hpp @@ -11,6 +11,7 @@ # define BOOST_PYTHON_MODULE BOOST_PYTHON_MODULE_INIT # if PY_VERSION_HEX >= 0x03050000 # define BOOST_PYTHON_MODULE_MULTI_PHASE BOOST_PYTHON_MODULE_MULTI_PHASE_INIT +# define BOOST_PYTHON_MODULE_WITH_STATE BOOST_PYTHON_MODULE_WITH_STATE_INIT # endif #endif // MODULE_DWA20011221_HPP diff --git a/include/boost/python/module_init.hpp b/include/boost/python/module_init.hpp index 0d826be654..910421d4ec 100644 --- a/include/boost/python/module_init.hpp +++ b/include/boost/python/module_init.hpp @@ -9,6 +9,11 @@ # include # include +# if PY_VERSION_HEX >= 0x03050000 +# include +# include +# endif + # ifndef BOOST_PYTHON_MODULE_INIT namespace boost { namespace python { @@ -160,6 +165,44 @@ BOOST_PYTHON_DECL PyObject* init_module(char const* name, void(*)()); return PyModuleDef_Init(&moduledef); \ } \ void BOOST_PP_CAT(init_module_, name)() +# define _BOOST_PYTHON_MODULE_WITH_STATE_INIT(name, StateType, ...) \ + int BOOST_PP_CAT(exec_module_,name)(PyObject* mod) \ + { \ + return boost::python::detail::exec_module( \ + mod, BOOST_PP_CAT(init_module_, name) ); \ + } \ + extern "C" BOOST_SYMBOL_EXPORT PyObject* BOOST_PP_CAT(PyInit_, name)() \ + { \ + BOOST_STATIC_ASSERT_MSG(boost::is_pod::value, \ + "Module State MUST be a plain POD structure!"); \ + static PyModuleDef_Base initial_m_base = { \ + PyObject_HEAD_INIT(NULL) \ + 0, /* m_init */ \ + 0, /* m_index */ \ + 0 /* m_copy */ }; \ + static PyMethodDef initial_methods[] = { { 0, 0, 0, 0 } }; \ + \ + static PyModuleDef_Slot slots[] = { \ + {Py_mod_exec, reinterpret_cast(reinterpret_cast(BOOST_PP_CAT(exec_module_, name)))}, \ + {Py_mod_gil, boost::python::detail::gil_not_used_option(__VA_ARGS__) ? Py_MOD_GIL_NOT_USED : Py_MOD_GIL_USED}, \ + {0, NULL} \ + }; \ + \ + static struct PyModuleDef moduledef = { \ + initial_m_base, \ + BOOST_PP_STRINGIZE(name), \ + 0, /* m_doc */ \ + sizeof(StateType), /* m_size */ \ + initial_methods, \ + slots, /* m_slots */ \ + 0, /* m_traverse */ \ + 0, /* m_clear */ \ + 0, /* m_free */ \ + }; \ + \ + return PyModuleDef_Init(&moduledef); \ + } \ + void BOOST_PP_CAT(init_module_, name)() # else // ! HAS_CXX11 && Python 3.13+ # define _BOOST_PYTHON_MODULE_MULTI_PHASE_INIT(name) \ int BOOST_PP_CAT(exec_module_,name)(PyObject* mod) \ @@ -196,6 +239,43 @@ BOOST_PYTHON_DECL PyObject* init_module(char const* name, void(*)()); return PyModuleDef_Init(&moduledef); \ } \ void BOOST_PP_CAT(init_module_, name)() +# define _BOOST_PYTHON_MODULE_WITH_STATE_INIT(name, StateType) \ + int BOOST_PP_CAT(exec_module_,name)(PyObject* mod) \ + { \ + return boost::python::detail::exec_module( \ + mod, BOOST_PP_CAT(init_module_, name) ); \ + } \ + extern "C" BOOST_SYMBOL_EXPORT PyObject* BOOST_PP_CAT(PyInit_, name)() \ + { \ + BOOST_STATIC_ASSERT_MSG(boost::is_pod::value, \ + "Module State MUST be a plain POD structure!"); \ + static PyModuleDef_Base initial_m_base = { \ + PyObject_HEAD_INIT(NULL) \ + 0, /* m_init */ \ + 0, /* m_index */ \ + 0 /* m_copy */ }; \ + static PyMethodDef initial_methods[] = { { 0, 0, 0, 0 } }; \ + \ + static PyModuleDef_Slot slots[] = { \ + {Py_mod_exec, reinterpret_cast(reinterpret_cast(BOOST_PP_CAT(exec_module_, name)))}, \ + {0, NULL} \ + }; \ + \ + static struct PyModuleDef moduledef = { \ + initial_m_base, \ + BOOST_PP_STRINGIZE(name), \ + 0, /* m_doc */ \ + sizeof(StateType), /* m_size */ \ + initial_methods, \ + slots, /* m_slots */ \ + 0, /* m_traverse */ \ + 0, /* m_clear */ \ + 0, /* m_free */ \ + }; \ + \ + return PyModuleDef_Init(&moduledef); \ + } \ + void BOOST_PP_CAT(init_module_, name)() # endif // HAS_CXX11 && Python 3.13+ # endif // PY_VERSION_HEX >= 0x03050000 @@ -227,10 +307,16 @@ extern "C" BOOST_SYMBOL_EXPORT _BOOST_PYTHON_MODULE_INIT(name) # define BOOST_PYTHON_MODULE_MULTI_PHASE_INIT(name, ...) \ void BOOST_PP_CAT(init_module_,name)(); \ extern "C" BOOST_SYMBOL_EXPORT _BOOST_PYTHON_MODULE_MULTI_PHASE_INIT(name, __VA_ARGS__) +# define BOOST_PYTHON_MODULE_WITH_STATE_INIT(name, StateType, ...) \ + void BOOST_PP_CAT(init_module_,name)(); \ +extern "C" BOOST_SYMBOL_EXPORT _BOOST_PYTHON_MODULE_WITH_STATE_INIT(name, StateType, __VA_ARGS__) # else # define BOOST_PYTHON_MODULE_MULTI_PHASE_INIT(name) \ void BOOST_PP_CAT(init_module_,name)(); \ extern "C" BOOST_SYMBOL_EXPORT _BOOST_PYTHON_MODULE_MULTI_PHASE_INIT(name) +# define BOOST_PYTHON_MODULE_WITH_STATE_INIT(name, StateType) \ + void BOOST_PP_CAT(init_module_,name)(); \ +extern "C" BOOST_SYMBOL_EXPORT _BOOST_PYTHON_MODULE_WITH_STATE_INIT(name) # endif // HAS_CXX11 && Python 3.13+ # endif // PY_VERSION_HEX >= 0x03050000 From afd27c63d5b7e261cfe0ef2bc45414d65b2ad81d Mon Sep 17 00:00:00 2001 From: O01eg <397177+o01eg@users.noreply.github.com> Date: Fri, 26 Jun 2026 09:09:01 +0400 Subject: [PATCH 08/14] Provide 'state' accessor in module definition --- include/boost/python/module_init.hpp | 16 +++++++------ src/module.cpp | 36 ++++++++++++++++++++++++++++ 2 files changed, 45 insertions(+), 7 deletions(-) diff --git a/include/boost/python/module_init.hpp b/include/boost/python/module_init.hpp index 910421d4ec..0478cd9c48 100644 --- a/include/boost/python/module_init.hpp +++ b/include/boost/python/module_init.hpp @@ -56,6 +56,8 @@ BOOST_PYTHON_DECL PyObject* init_module(PyModuleDef&, void(*)(), bool gil_not_us BOOST_PYTHON_DECL int exec_module(PyObject*, void(*)()); +BOOST_PYTHON_DECL int exec_module_with_state(PyObject*, void(*)(void*)); + # endif // PY_VERSION_HEX >= 0x03050000 # else // PY_VERSION_HEX >= 0x03000000 @@ -169,7 +171,7 @@ BOOST_PYTHON_DECL PyObject* init_module(char const* name, void(*)()); int BOOST_PP_CAT(exec_module_,name)(PyObject* mod) \ { \ return boost::python::detail::exec_module( \ - mod, BOOST_PP_CAT(init_module_, name) ); \ + mod, reinterpret_cast(BOOST_PP_CAT(init_module_, name)) ); \ } \ extern "C" BOOST_SYMBOL_EXPORT PyObject* BOOST_PP_CAT(PyInit_, name)() \ { \ @@ -202,7 +204,7 @@ BOOST_PYTHON_DECL PyObject* init_module(char const* name, void(*)()); \ return PyModuleDef_Init(&moduledef); \ } \ - void BOOST_PP_CAT(init_module_, name)() + void BOOST_PP_CAT(init_module_, name)(StateType* state) # else // ! HAS_CXX11 && Python 3.13+ # define _BOOST_PYTHON_MODULE_MULTI_PHASE_INIT(name) \ int BOOST_PP_CAT(exec_module_,name)(PyObject* mod) \ @@ -243,7 +245,7 @@ BOOST_PYTHON_DECL PyObject* init_module(char const* name, void(*)()); int BOOST_PP_CAT(exec_module_,name)(PyObject* mod) \ { \ return boost::python::detail::exec_module( \ - mod, BOOST_PP_CAT(init_module_, name) ); \ + mod, reinterpret_cast(BOOST_PP_CAT(init_module_, name)) ); \ } \ extern "C" BOOST_SYMBOL_EXPORT PyObject* BOOST_PP_CAT(PyInit_, name)() \ { \ @@ -275,7 +277,7 @@ BOOST_PYTHON_DECL PyObject* init_module(char const* name, void(*)()); \ return PyModuleDef_Init(&moduledef); \ } \ - void BOOST_PP_CAT(init_module_, name)() + void BOOST_PP_CAT(init_module_, name)(StateType* state) # endif // HAS_CXX11 && Python 3.13+ # endif // PY_VERSION_HEX >= 0x03050000 @@ -308,15 +310,15 @@ extern "C" BOOST_SYMBOL_EXPORT _BOOST_PYTHON_MODULE_INIT(name) void BOOST_PP_CAT(init_module_,name)(); \ extern "C" BOOST_SYMBOL_EXPORT _BOOST_PYTHON_MODULE_MULTI_PHASE_INIT(name, __VA_ARGS__) # define BOOST_PYTHON_MODULE_WITH_STATE_INIT(name, StateType, ...) \ - void BOOST_PP_CAT(init_module_,name)(); \ + void BOOST_PP_CAT(init_module_,name)(StateType* state); \ extern "C" BOOST_SYMBOL_EXPORT _BOOST_PYTHON_MODULE_WITH_STATE_INIT(name, StateType, __VA_ARGS__) # else # define BOOST_PYTHON_MODULE_MULTI_PHASE_INIT(name) \ void BOOST_PP_CAT(init_module_,name)(); \ extern "C" BOOST_SYMBOL_EXPORT _BOOST_PYTHON_MODULE_MULTI_PHASE_INIT(name) # define BOOST_PYTHON_MODULE_WITH_STATE_INIT(name, StateType) \ - void BOOST_PP_CAT(init_module_,name)(); \ -extern "C" BOOST_SYMBOL_EXPORT _BOOST_PYTHON_MODULE_WITH_STATE_INIT(name) + void BOOST_PP_CAT(init_module_,name)(StateType* state); \ +extern "C" BOOST_SYMBOL_EXPORT _BOOST_PYTHON_MODULE_WITH_STATE_INIT(name, StateType) # endif // HAS_CXX11 && Python 3.13+ # endif // PY_VERSION_HEX >= 0x03050000 diff --git a/src/module.cpp b/src/module.cpp index 22c4c5868a..200b0bb787 100644 --- a/src/module.cpp +++ b/src/module.cpp @@ -26,6 +26,34 @@ namespace return m; } +# if PY_VERSION_HEX >= 0x03050000 + class init_function_with_state { + public: + init_function_with_state(void(*init_function)(void*), void* state) + : init_function_(init_function), state_(state) {} + + void operator()() const { init_function_(state_); } + private: + void(*const init_function_)(void*); + void* const state_; + }; + + PyObject* init_module_in_scope_with_state(PyObject* m, void(*init_function)(void*)) + { + if (m != 0) + { + // Create the current module scope + object m_obj(((borrowed_reference_t*)m)); + scope current_module(m_obj); + + void* state = PyModule_GetState(m); + + if (handle_exception(init_function_with_state(init_function, state))) return NULL; + } + + return m; + } +# endif } BOOST_PYTHON_DECL void scope_setattr_doc(char const* name, object const& x, char const* doc) @@ -62,6 +90,14 @@ BOOST_PYTHON_DECL int exec_module(PyObject* mod, void(*init_function)()) return retval ? 0 : -1; } +BOOST_PYTHON_DECL int exec_module_with_state(PyObject* mod, void(*init_function)(void*)) +{ + PyObject* retval = init_module_in_scope_with_state( + mod, + init_function); + return retval ? 0 : -1; +} + # endif #else From c86ea207d1a7eb84c34f7b606e62a7a22b43538d Mon Sep 17 00:00:00 2001 From: O01eg <397177+o01eg@users.noreply.github.com> Date: Sat, 27 Jun 2026 00:16:21 +0400 Subject: [PATCH 09/14] Fix and add tests for module state --- include/boost/python/module_init.hpp | 4 ++-- test/fabscript | 2 ++ test/module_multi_phase_state.cpp | 30 ++++++++++++++++++++++++++++ test/module_multi_phase_state.py | 23 +++++++++++++++++++++ 4 files changed, 57 insertions(+), 2 deletions(-) create mode 100644 test/module_multi_phase_state.cpp create mode 100644 test/module_multi_phase_state.py diff --git a/include/boost/python/module_init.hpp b/include/boost/python/module_init.hpp index 0478cd9c48..abf1ce2524 100644 --- a/include/boost/python/module_init.hpp +++ b/include/boost/python/module_init.hpp @@ -170,7 +170,7 @@ BOOST_PYTHON_DECL PyObject* init_module(char const* name, void(*)()); # define _BOOST_PYTHON_MODULE_WITH_STATE_INIT(name, StateType, ...) \ int BOOST_PP_CAT(exec_module_,name)(PyObject* mod) \ { \ - return boost::python::detail::exec_module( \ + return boost::python::detail::exec_module_with_state( \ mod, reinterpret_cast(BOOST_PP_CAT(init_module_, name)) ); \ } \ extern "C" BOOST_SYMBOL_EXPORT PyObject* BOOST_PP_CAT(PyInit_, name)() \ @@ -244,7 +244,7 @@ BOOST_PYTHON_DECL PyObject* init_module(char const* name, void(*)()); # define _BOOST_PYTHON_MODULE_WITH_STATE_INIT(name, StateType) \ int BOOST_PP_CAT(exec_module_,name)(PyObject* mod) \ { \ - return boost::python::detail::exec_module( \ + return boost::python::detail::exec_module_with_state( \ mod, reinterpret_cast(BOOST_PP_CAT(init_module_, name)) ); \ } \ extern "C" BOOST_SYMBOL_EXPORT PyObject* BOOST_PP_CAT(PyInit_, name)() \ diff --git a/test/fabscript b/test/fabscript index f418ed3efb..c6d834aa0d 100644 --- a/test/fabscript +++ b/test/fabscript @@ -180,5 +180,7 @@ tests.append(extension_test("module_multi_phase", condition=python_version_major > 3 or (python_version_major == 3 and python_version_minor >= 5))) tests.append(extension_test("module_multi_phase_nogil", condition=python_version_major > 3 or (python_version_major == 3 and python_version_minor >= 5))) +tests.append(extension_test("module_multi_phase_state", + condition=python_version_major > 3 or (python_version_major == 3 and python_version_minor >= 5))) default = report('report', tests, fail_on_failures=True) diff --git a/test/module_multi_phase_state.cpp b/test/module_multi_phase_state.cpp new file mode 100644 index 0000000000..0e497fc9cf --- /dev/null +++ b/test/module_multi_phase_state.cpp @@ -0,0 +1,30 @@ +// Distributed under the Boost Software License, Version 1.0. (See +// accompanying file LICENSE_1_0.txt or copy at +// http://www.boost.org/LICENSE_1_0.txt) + +#include +#include +#include + +using namespace boost::python; + +struct TestState +{ + int x; +}; + +int get_state_x() { + TestState* state = reinterpret_cast(PyModule_GetState(import("module_multi_phase_state_ext").ptr())); + if (state != nullptr) { + return state->x; + } + return -1; +} + +BOOST_PYTHON_MODULE_WITH_STATE(module_multi_phase_state_ext, TestState) +{ + state->x = 42; + def("x", get_state_x); +} + +#include "module_tail.cpp" diff --git a/test/module_multi_phase_state.py b/test/module_multi_phase_state.py new file mode 100644 index 0000000000..1df4b96235 --- /dev/null +++ b/test/module_multi_phase_state.py @@ -0,0 +1,23 @@ +# Distributed under the Boost +# Software License, Version 1.0. (See accompanying +# file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) +""" +>>> import module_multi_phase_state_ext +>>> module_multi_phase_state_ext.x() +42 +""" + +def run(args = None): + import sys + import doctest + + if args is not None: + sys.argv = args + return doctest.testmod(sys.modules.get(__name__)) + +if __name__ == '__main__': + print("running...") + import sys + status = run()[0] + if (status == 0): print("Done.") + sys.exit(status) From e08584a4ed0f87b3f2d6b590c3198a5df66f07c3 Mon Sep 17 00:00:00 2001 From: Eisuke Kawashima Date: Sun, 5 Apr 2026 17:36:56 +0900 Subject: [PATCH 10/14] feat(vector_indexing_suite): implement __iadd__ --- .../boost/python/suite/indexing/vector_indexing_suite.hpp | 8 ++++++++ test/vector_indexing_suite.py | 3 +++ 2 files changed, 11 insertions(+) diff --git a/include/boost/python/suite/indexing/vector_indexing_suite.hpp b/include/boost/python/suite/indexing/vector_indexing_suite.hpp index 34c29ecc6f..6ca31896ae 100644 --- a/include/boost/python/suite/indexing/vector_indexing_suite.hpp +++ b/include/boost/python/suite/indexing/vector_indexing_suite.hpp @@ -61,6 +61,7 @@ namespace boost { namespace python { extension_def(Class& cl) { cl + .def("__iadd__", &base_iadd) .def("append", &base_append) .def("extend", &base_extend) ; @@ -235,6 +236,13 @@ namespace boost { namespace python { container_utils::extend_container(temp, v); DerivedPolicies::extend(container, temp.begin(), temp.end()); } + + static object + base_iadd(Container& container, object v) + { + base_extend(container, v); + return object(container); + } }; }} // namespace boost::python diff --git a/test/vector_indexing_suite.py b/test/vector_indexing_suite.py index 478cd01516..5e5e8f7a3d 100644 --- a/test/vector_indexing_suite.py +++ b/test/vector_indexing_suite.py @@ -320,6 +320,9 @@ >>> v.extend(['f','g','h','i','j']) >>> print_xvec(v) [ a b c d e f g h i j ] +>>> v += ['k','l','m'] +>>> print_xvec(v) +[ a b c d e f g h i j k l m ] ##################################################################### # extend using a generator expression From d8668c5963fc085076828b77bfda75a52eb72289 Mon Sep 17 00:00:00 2001 From: Eisuke Kawashima Date: Sun, 5 Apr 2026 16:20:09 +0900 Subject: [PATCH 11/14] feat(indexing_suite): implement clear --- include/boost/python/suite/indexing/indexing_suite.hpp | 7 +++++++ include/boost/python/suite/indexing/map_indexing_suite.hpp | 6 ++++++ .../boost/python/suite/indexing/vector_indexing_suite.hpp | 6 ++++++ test/map_indexing_suite.py | 4 ++++ test/vector_indexing_suite.py | 4 ++++ 5 files changed, 27 insertions(+) diff --git a/include/boost/python/suite/indexing/indexing_suite.hpp b/include/boost/python/suite/indexing/indexing_suite.hpp index 3469a2a40f..140e3a665c 100644 --- a/include/boost/python/suite/indexing/indexing_suite.hpp +++ b/include/boost/python/suite/indexing/indexing_suite.hpp @@ -184,6 +184,7 @@ namespace boost { namespace python { .def("__getitem__", &base_get_item) .def("__contains__", &base_contains) .def("__iter__", def_iterator()) + .def("clear", &base_clear) ; DerivedPolicies::extension_def(cl); @@ -288,6 +289,12 @@ namespace boost { namespace python { return false; } } + + static void + base_clear(Container& container) + { + return DerivedPolicies::clear(container); + } }; }} // namespace boost::python diff --git a/include/boost/python/suite/indexing/map_indexing_suite.hpp b/include/boost/python/suite/indexing/map_indexing_suite.hpp index 7fbad4cace..567abe4e74 100644 --- a/include/boost/python/suite/indexing/map_indexing_suite.hpp +++ b/include/boost/python/suite/indexing/map_indexing_suite.hpp @@ -137,6 +137,12 @@ namespace boost { namespace python { container.erase(i); } + static void + clear(Container& container) + { + container.clear(); + } + static size_t size(Container& container) { diff --git a/include/boost/python/suite/indexing/vector_indexing_suite.hpp b/include/boost/python/suite/indexing/vector_indexing_suite.hpp index 6ca31896ae..264eac0133 100644 --- a/include/boost/python/suite/indexing/vector_indexing_suite.hpp +++ b/include/boost/python/suite/indexing/vector_indexing_suite.hpp @@ -135,6 +135,12 @@ namespace boost { namespace python { container.erase(container.begin()+from, container.begin()+to); } + static void + clear(Container& container) + { + container.clear(); + } + static size_t size(Container& container) { diff --git a/test/map_indexing_suite.py b/test/map_indexing_suite.py index 6d3e57a102..939d984c7b 100644 --- a/test/map_indexing_suite.py +++ b/test/map_indexing_suite.py @@ -202,6 +202,10 @@ ... dom = el.data() joel kimpo +>>> tm.clear() +>>> print_xmap(tm) +[ ] + ##################################################################### # Test custom converter... ##################################################################### diff --git a/test/vector_indexing_suite.py b/test/vector_indexing_suite.py index 5e5e8f7a3d..24d1caba42 100644 --- a/test/vector_indexing_suite.py +++ b/test/vector_indexing_suite.py @@ -337,6 +337,10 @@ >>> print_xvec(v) [ a b c d e f h i j ] +>>> v.clear() +>>> print_xvec(v) +[ ] + ##################################################################### # vector of strings ##################################################################### From 27ff6f6f37775792e98dcfd5a87258fc106e536c Mon Sep 17 00:00:00 2001 From: Eisuke Kawashima Date: Sun, 5 Apr 2026 16:13:45 +0900 Subject: [PATCH 12/14] feat(vector_indexing_suite): implement count --- .../suite/indexing/vector_indexing_suite.hpp | 16 ++++++++++++++++ test/vector_indexing_suite.py | 7 +++++++ 2 files changed, 23 insertions(+) diff --git a/include/boost/python/suite/indexing/vector_indexing_suite.hpp b/include/boost/python/suite/indexing/vector_indexing_suite.hpp index 264eac0133..7cbdd2c6e2 100644 --- a/include/boost/python/suite/indexing/vector_indexing_suite.hpp +++ b/include/boost/python/suite/indexing/vector_indexing_suite.hpp @@ -63,6 +63,7 @@ namespace boost { namespace python { cl .def("__iadd__", &base_iadd) .def("append", &base_append) + .def("count", &base_count) .def("extend", &base_extend) ; } @@ -208,6 +209,21 @@ namespace boost { namespace python { } private: + + static size_t + base_count(Container& container, object v) + { + extract elem(v); + if (elem.check()) { + return std::count(container.begin(), container.end(), elem()); + } else { + extract elem(v); + if (!elem.check()) { + return 0; + } + return std::count(container.begin(), container.end(), elem()); + } + } static void base_append(Container& container, object v) diff --git a/test/vector_indexing_suite.py b/test/vector_indexing_suite.py index 24d1caba42..6abca6140a 100644 --- a/test/vector_indexing_suite.py +++ b/test/vector_indexing_suite.py @@ -297,6 +297,13 @@ >>> assert not 12345 in v ##################################################################### +# Count +##################################################################### +>>> v.count('a') +1 +>>> v.count(12345) +0 + # Show that iteration allows mutable access to the elements ##################################################################### >>> v[:] = ['a','b','c','d','e'] # reset again From 660e8944ffcfd02890e3cfdff4c168219f65a495 Mon Sep 17 00:00:00 2001 From: Eisuke Kawashima Date: Sun, 5 Apr 2026 17:50:35 +0900 Subject: [PATCH 13/14] feat(vector_indexing_suite): implement remove --- .../suite/indexing/vector_indexing_suite.hpp | 37 +++++++++++++++++++ test/vector_indexing_suite.py | 8 ++++ 2 files changed, 45 insertions(+) diff --git a/include/boost/python/suite/indexing/vector_indexing_suite.hpp b/include/boost/python/suite/indexing/vector_indexing_suite.hpp index 7cbdd2c6e2..fd5d86c406 100644 --- a/include/boost/python/suite/indexing/vector_indexing_suite.hpp +++ b/include/boost/python/suite/indexing/vector_indexing_suite.hpp @@ -65,6 +65,7 @@ namespace boost { namespace python { .def("append", &base_append) .def("count", &base_count) .def("extend", &base_extend) + .def("remove", &base_remove) ; } @@ -265,6 +266,42 @@ namespace boost { namespace python { base_extend(container, v); return object(container); } + + static void + base_remove(Container& container, object v) + { + extract key(v); + if (key.check()) + { + auto i = std::find(container.begin(), container.end(), key()); + if (i == container.end()) + { + PyErr_SetString(PyExc_ValueError, "remove(x): x not in vector_indexing_suite"); + throw_error_already_set(); + } + container.erase(i); + } + else + { + extract key(v); + if (key.check()) + { + auto i = std::find(container.begin(), container.end(), key()); + if (i == container.end()) + { + PyErr_SetString(PyExc_ValueError, "remove(x): x not in vector_indexing_suite"); + throw_error_already_set(); + } + container.erase(i); + } + else + { + PyErr_SetString(PyExc_TypeError, + "Attempting to remove an invalid type"); + throw_error_already_set(); + } + } + } }; }} // namespace boost::python diff --git a/test/vector_indexing_suite.py b/test/vector_indexing_suite.py index 6abca6140a..c0b72c6ab9 100644 --- a/test/vector_indexing_suite.py +++ b/test/vector_indexing_suite.py @@ -78,6 +78,14 @@ >>> print_xvec(v) [ yaba c d e ] +>>> v2 = XVec() +>>> v2[:] = [X('b'), X('a'), X('c'), X('b'), X('a')] +>>> try: v2.remove("z") +... except ValueError: pass +>>> v2.remove("a") +>>> print_xvec(v2) +[ b c b a ] + ##################################################################### # Calling a mutating function of a container element ##################################################################### From fe087f9c2ff586d9b105c3c4507e2d913238a106 Mon Sep 17 00:00:00 2001 From: Eisuke Kawashima Date: Sun, 5 Apr 2026 16:29:32 +0900 Subject: [PATCH 14/14] feat(vector_indexing_suite): implement reverse --- .../python/suite/indexing/vector_indexing_suite.hpp | 10 ++++++++++ test/vector_indexing_suite.py | 8 ++++++++ 2 files changed, 18 insertions(+) diff --git a/include/boost/python/suite/indexing/vector_indexing_suite.hpp b/include/boost/python/suite/indexing/vector_indexing_suite.hpp index fd5d86c406..f1b27c7381 100644 --- a/include/boost/python/suite/indexing/vector_indexing_suite.hpp +++ b/include/boost/python/suite/indexing/vector_indexing_suite.hpp @@ -66,6 +66,7 @@ namespace boost { namespace python { .def("count", &base_count) .def("extend", &base_extend) .def("remove", &base_remove) + .def("reverse", &base_reverse) ; } @@ -302,6 +303,15 @@ namespace boost { namespace python { } } } + + static void + base_reverse(Container& container) + { + using std::swap; + const unsigned n = size(container); + for (unsigned i = 0; i < n / 2; i++) + swap(container[i], container[n - i - 1]); + } }; }} // namespace boost::python diff --git a/test/vector_indexing_suite.py b/test/vector_indexing_suite.py index c0b72c6ab9..fb2b92a1ee 100644 --- a/test/vector_indexing_suite.py +++ b/test/vector_indexing_suite.py @@ -312,6 +312,14 @@ >>> v.count(12345) 0 +##################################################################### +# Reverse +##################################################################### +>>> v.reverse() +>>> print_xvec(v) +[ e d c b a ] + +##################################################################### # Show that iteration allows mutable access to the elements ##################################################################### >>> v[:] = ['a','b','c','d','e'] # reset again