diff --git a/.github/workflows/test-windows.yml b/.github/workflows/test-windows.yml
index 576e1f441..d13b6bf86 100644
--- a/.github/workflows/test-windows.yml
+++ b/.github/workflows/test-windows.yml
@@ -10,40 +10,77 @@ jobs:
matrix:
python-version: [3.7]
+ env:
+ VCPKG_ROOT: '${{ github.workspace }}/vcpkg'
+ VCPKG_ASSET_SOURCES: "${{ github.workspace }}/vcpkgAssets"
+ X_VCPKG_ASSET_SOURCES: clear;x-azurl,file:///${{ github.workspace }}/vcpkgAssets,,readwrite
+ VCPKG_BINARY_SOURCES: clear;files,${{ github.workspace }}/vcpkgBin,readwrite
+ VCPKG_BUILD_TREES: "${{ github.workspace }}/vcpkgBuild"
+ VCPKG_INSTALLED_DIR: "${{ github.workspace }}/vcpkgInstalled"
+ VCPKG_INSTALL_OPTIONS: "--x-buildtrees-root=${{ github.workspace }}/vcpkgBuild"
+ VCPKG_DEFAULT_TRIPLET: 'x64-windows'
+ VCPKG_DEFAULT_HOST_TRIPLET: 'x64-windows'
+
steps:
- - uses: actions/checkout@v5
+ - uses: actions/checkout@9c091bb21b7c1c1d1991bb908d89e4e9dddfe3e0 # v7
+
+ - name: Restore vcpkg cache
+ id: cache-vcpkg-restore
+ uses: actions/cache/restore@27d5ce7f107fe9357f9df03efb73ab90386fccae # v5.0.5
+ with:
+ path: |
+ "${{ github.workspace }}/vcpkgAssets"
+ "${{ github.workspace }}/vcpkgBCache"
+ "${{ github.workspace }}/vcpkgBin"
+ "${{ github.workspace }}/vcpkgBuild"
+ key: vcpkgCache-${{ github.repository_owner }}-${{ matrix.os }}-${{ matrix.triplet }}-${{ hashFiles('vcpkg.json') }}
+
+ - name: Create directories on cache miss
+ run: |
+ mkdir -p "${{ github.workspace }}/vcpkgAssets"
+ mkdir -p "${{ github.workspace }}/vcpkgBCache"
+ mkdir -p "${{ github.workspace }}/vcpkgBin"
+ mkdir -p "${{ github.workspace }}/vcpkgBuild"
+ if: steps.cache-vcpkg-restore.outputs.cache-hit != 'true'
+
- uses: actions/setup-python@v6
with:
python-version: ${{ matrix.python-version }}
- - uses: microsoft/setup-msbuild@v2
+
+ - uses: microsoft/setup-msbuild@30375c66a4eea26614e0d39710365f22f8b0af57 # v3.0.0
+
- name: setup boost prerequisites
- uses: lukka/run-vcpkg@v6
+ uses: lukka/run-vcpkg@b1a0dd252f06b9e25b3c022a9a03bd7a427fb6a2 # v11.6
with:
- vcpkgGitCommitId: '88b1071e39f13b632644d9d953738d345a4ac055'
- vcpkgDirectory: '${{ runner.workspace }}/vcpkg'
- vcpkgTriplet: x64-windows
- vcpkgArguments: >
- boost-config
- boost-core
- boost-function
- boost-graph
- boost-iterator
- boost-lexical-cast
- boost-mpl
- boost-preprocessor
- boost-smart-ptr
- boost-static-assert
- boost-align
+ vcpkgDirectory: '${{ github.workspace }}/vcpkg'
+ binaryCachePath: "${{ github.workspace }}/vcpkgBCache"
+ runVcpkgInstall: true
+ vcpkgJsonGlob: .github/workflows/vcpkg.json
+
- name: setup faber
run: |
python -m pip install --upgrade pip
python -m pip install setuptools faber numpy
faber --info=tools cxx
+
- name: build
shell: cmd
run: |
- faber --builddir=build cxx.name=msvc --log=commands --log=output --with-boost-include=${{ runner.workspace }}\vcpkg\installed\x64-windows\include -j4
+ faber --builddir=build cxx.name=msvc --log=commands --log=output --with-boost-include="${{ github.workspace }}/vcpkgInstalled/x64-windows/include" -j4
+
- name: test
shell: cmd
run: |
- faber --builddir=build cxx.name=msvc --with-boost-include=${{ runner.workspace }}\vcpkg\installed\x64-windows\include -j4 test.report
+ faber --builddir=build cxx.name=msvc --with-boost-include="${{ github.workspace }}/vcpkgInstalled/x64-windows/include" -j4 test.report
+
+ - name: Save vcpkg cache
+ id: cache-vcpkg-save
+ uses: actions/cache/save@27d5ce7f107fe9357f9df03efb73ab90386fccae # v5.0.5
+ with:
+ path: |
+ "${{ github.workspace }}/vcpkgAssets"
+ "${{ github.workspace }}/vcpkgBCache"
+ "${{ github.workspace }}/vcpkgBin"
+ "${{ github.workspace }}/vcpkgBuild"
+ key: ${{ steps.cache-vcpkg-restore.outputs.cache-primary-key }}
+ if: steps.cache-vcpkg-restore.outputs.cache-hit != 'true'
diff --git a/.github/workflows/vcpkg.json b/.github/workflows/vcpkg.json
new file mode 100644
index 000000000..5a9e71d23
--- /dev/null
+++ b/.github/workflows/vcpkg.json
@@ -0,0 +1,20 @@
+{
+ "$schema": "https://raw.githubusercontent.com/microsoft/vcpkg/master/scripts/vcpkg.schema.json",
+ "name": "boost-python",
+ "version-string": "1.91.0",
+ "builtin-baseline": "f3e10653cc27d62a37a3763cd84b38bca07c6075",
+ "dependencies": [
+ "boost-config",
+ "boost-core",
+ "boost-function",
+ "boost-graph",
+ "boost-iterator",
+ "boost-lexical-cast",
+ "boost-mpl",
+ "boost-preprocessor",
+ "boost-smart-ptr",
+ "boost-static-assert",
+ "boost-align",
+ "python3"
+ ]
+}
diff --git a/CMakeLists.txt b/CMakeLists.txt
index 299ef84e9..71bad1552 100644
--- a/CMakeLists.txt
+++ b/CMakeLists.txt
@@ -71,7 +71,6 @@ target_link_libraries(${_boost_python}
Boost::numeric_conversion
Boost::preprocessor
Boost::smart_ptr
- Boost::static_assert
Boost::tuple
Boost::type_traits
Boost::utility
diff --git a/build.jam b/build.jam
index e9eb1a11a..55bed73ba 100644
--- a/build.jam
+++ b/build.jam
@@ -19,7 +19,6 @@ constant boost_dependencies :
/boost/mpl//boost_mpl
/boost/numeric_conversion//boost_numeric_conversion
/boost/preprocessor//boost_preprocessor
- /boost/static_assert//boost_static_assert
/boost/tuple//boost_tuple
/boost/type_traits//boost_type_traits
/boost/utility//boost_utility ;
diff --git a/doc/numpy/_templates/layout.html b/doc/numpy/_templates/layout.html
index 69e1a868c..89f1ca0b3 100644
--- a/doc/numpy/_templates/layout.html
+++ b/doc/numpy/_templates/layout.html
@@ -47,7 +47,7 @@
};
{%- for scriptfile in script_files %}
-
+
{%- endfor %}
diff --git a/include/boost/python/module.hpp b/include/boost/python/module.hpp
index 8ad69f5a3..4ec34868b 100644
--- a/include/boost/python/module.hpp
+++ b/include/boost/python/module.hpp
@@ -9,5 +9,9 @@
# 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
+# 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 390db82cf..abf1ce252 100644
--- a/include/boost/python/module_init.hpp
+++ b/include/boost/python/module_init.hpp
@@ -9,11 +9,16 @@
# include
# include
+# if PY_VERSION_HEX >= 0x03050000
+# include
+# include
+# endif
+
# ifndef BOOST_PYTHON_MODULE_INIT
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 +44,7 @@ inline bool gil_not_used_option(F &&, O &&...o) {
}
}
-#endif // HAS_CXX11
+# endif // HAS_CXX11
namespace detail {
@@ -47,11 +52,19 @@ namespace detail {
BOOST_PYTHON_DECL PyObject* init_module(PyModuleDef&, void(*)(), bool gil_not_used = false);
-#else
+# if PY_VERSION_HEX >= 0x03050000
+
+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
BOOST_PYTHON_DECL PyObject* init_module(char const* name, void(*)());
-#endif
+# endif // PY_VERSION_HEX >= 0x03000000
}}}
@@ -115,7 +128,161 @@ BOOST_PYTHON_DECL PyObject* init_module(char const* name, void(*)());
void BOOST_PP_CAT(init_module_, name)()
# endif // HAS_CXX11
-# else
+# if PY_VERSION_HEX >= 0x03050000
+
+# if defined(HAS_CXX11) && (PY_VERSION_HEX >= 0x030D0000)
+# define _BOOST_PYTHON_MODULE_MULTI_PHASE_INIT(name, ...) \
+ 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)() \
+ { \
+ 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 */ \
+ 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)()
+# define _BOOST_PYTHON_MODULE_WITH_STATE_INIT(name, StateType, ...) \
+ int BOOST_PP_CAT(exec_module_,name)(PyObject* mod) \
+ { \
+ 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)() \
+ { \
+ 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)(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) \
+ { \
+ return boost::python::detail::exec_module( \
+ mod, 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)()
+# define _BOOST_PYTHON_MODULE_WITH_STATE_INIT(name, StateType) \
+ int BOOST_PP_CAT(exec_module_,name)(PyObject* mod) \
+ { \
+ 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)() \
+ { \
+ 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)(StateType* state)
+# endif // HAS_CXX11 && Python 3.13+
+
+# endif // PY_VERSION_HEX >= 0x03050000
+
+# else // ! PY_VERSION_HEX >= 0x03000000
# define _BOOST_PYTHON_MODULE_INIT(name) \
void BOOST_PP_CAT(init,name)() \
@@ -125,7 +292,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, ...) \
@@ -137,6 +304,24 @@ 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
-# endif
+# if PY_VERSION_HEX >= 0x03050000
+# 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__)
+# define BOOST_PYTHON_MODULE_WITH_STATE_INIT(name, StateType, ...) \
+ 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)(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
+
+# endif // BOOST_PYTHON_MODULE_INIT
#endif // MODULE_INIT_DWA20020722_HPP
diff --git a/include/boost/python/suite/indexing/indexing_suite.hpp b/include/boost/python/suite/indexing/indexing_suite.hpp
index 3469a2a40..140e3a665 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 7fbad4cac..567abe4e7 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 34c29ecc6..f1b27c738 100644
--- a/include/boost/python/suite/indexing/vector_indexing_suite.hpp
+++ b/include/boost/python/suite/indexing/vector_indexing_suite.hpp
@@ -61,8 +61,12 @@ namespace boost { namespace python {
extension_def(Class& cl)
{
cl
+ .def("__iadd__", &base_iadd)
.def("append", &base_append)
+ .def("count", &base_count)
.def("extend", &base_extend)
+ .def("remove", &base_remove)
+ .def("reverse", &base_reverse)
;
}
@@ -134,6 +138,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)
{
@@ -201,6 +211,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)
@@ -235,6 +260,58 @@ 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);
+ }
+
+ 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();
+ }
+ }
+ }
+
+ 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/src/converter/builtin_converters.cpp b/src/converter/builtin_converters.cpp
index ee2d5b479..227fe2f80 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
diff --git a/src/module.cpp b/src/module.cpp
index c32f4187b..200b0bb78 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)
@@ -52,6 +80,26 @@ BOOST_PYTHON_DECL PyObject* init_module(PyModuleDef& moduledef,
init_function);
}
+# if PY_VERSION_HEX >= 0x03050000
+
+BOOST_PYTHON_DECL int exec_module(PyObject* mod, void(*init_function)())
+{
+ PyObject* retval = init_module_in_scope(
+ mod,
+ 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
namespace
diff --git a/test/Jamfile b/test/Jamfile
index 40115d86c..265658892 100644
--- a/test/Jamfile
+++ b/test/Jamfile
@@ -63,7 +63,6 @@ alias base_deps : usage-requirements
/boost/function//boost_function
/boost/mpl//boost_mpl
/boost/preprocessor//boost_preprocessor
- /boost/static_assert//boost_static_assert
/boost/type_traits//boost_type_traits
;
test-suite python
diff --git a/test/fabscript b/test/fabscript
index 7cf22f9c0..c6d834aa0 100644
--- a/test/fabscript
+++ b/test/fabscript
@@ -174,4 +174,13 @@ 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)))
+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/map_indexing_suite.py b/test/map_indexing_suite.py
index 6d3e57a10..939d984c7 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/module_multi_phase.cpp b/test/module_multi_phase.cpp
new file mode 100644
index 000000000..dcbbe7430
--- /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 000000000..138f5d982
--- /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)
diff --git a/test/module_multi_phase_nogil.cpp b/test/module_multi_phase_nogil.cpp
new file mode 100644
index 000000000..6de74c29d
--- /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 000000000..30c6bcb98
--- /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)
diff --git a/test/module_multi_phase_state.cpp b/test/module_multi_phase_state.cpp
new file mode 100644
index 000000000..0e497fc9c
--- /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 000000000..1df4b9623
--- /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)
diff --git a/test/vector_indexing_suite.py b/test/vector_indexing_suite.py
index 478cd0151..fb2b92a1e 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
#####################################################################
@@ -296,6 +304,21 @@
>>> assert not 'X' in v
>>> assert not 12345 in v
+#####################################################################
+# Count
+#####################################################################
+>>> v.count('a')
+1
+>>> v.count(12345)
+0
+
+#####################################################################
+# Reverse
+#####################################################################
+>>> v.reverse()
+>>> print_xvec(v)
+[ e d c b a ]
+
#####################################################################
# Show that iteration allows mutable access to the elements
#####################################################################
@@ -320,6 +343,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
@@ -334,6 +360,10 @@
>>> print_xvec(v)
[ a b c d e f h i j ]
+>>> v.clear()
+>>> print_xvec(v)
+[ ]
+
#####################################################################
# vector of strings
#####################################################################