From cb9bcfc0953b10e89e6c12743bd6f20865ec72fe Mon Sep 17 00:00:00 2001 From: symscae Date: Wed, 24 Jun 2026 10:44:06 -0700 Subject: [PATCH 01/18] updated cpp version and replaced boost smart pointer usages --- setup.py | 4 ++-- src/extensions/srreal_pickling.hpp | 2 +- src/extensions/wrap_AtomicStructureAdapter.cpp | 8 ++++---- src/extensions/wrap_StructureAdapter.cpp | 6 +++--- 4 files changed, 10 insertions(+), 10 deletions(-) diff --git a/setup.py b/setup.py index ab1f8587..c8d12d65 100644 --- a/setup.py +++ b/setup.py @@ -90,11 +90,11 @@ def get_objcryst_libraries(): if os.name == "nt": - compile_args = ["/std:c++14"] + compile_args = ["/std:c++23"] macros = [("_USE_MATH_DEFINES", None)] extra_link_args = ["/FORCE:MULTIPLE"] else: - compile_args = ["-std=c++11"] + compile_args = ["-std=c++23"] macros = [] extra_link_args = [] diff --git a/src/extensions/srreal_pickling.hpp b/src/extensions/srreal_pickling.hpp index 17068f46..7acf5449 100644 --- a/src/extensions/srreal_pickling.hpp +++ b/src/extensions/srreal_pickling.hpp @@ -245,7 +245,7 @@ class StructureAdapterPickleSuite : public boost::python::pickle_suite static bool frompython( diffpy::srreal::StructureAdapterPtr adpt) { - return bool(boost::dynamic_pointer_cast(adpt)); + return bool(std::dynamic_pointer_cast(adpt)); } }; // class StructureAdapterPickleSuite diff --git a/src/extensions/wrap_AtomicStructureAdapter.cpp b/src/extensions/wrap_AtomicStructureAdapter.cpp index e3ce6b39..e6b3953a 100644 --- a/src/extensions/wrap_AtomicStructureAdapter.cpp +++ b/src/extensions/wrap_AtomicStructureAdapter.cpp @@ -390,7 +390,7 @@ class MakeWrapper : public T, public wrapper_srreal // Wrapper helpers for class AtomicStructureAdapter typedef MakeWrapper AtomicStructureAdapterWrap; -typedef boost::shared_ptr AtomicStructureAdapterWrapPtr; +typedef std::shared_ptr AtomicStructureAdapterWrapPtr; class atomadapter_indexing : public vector_indexing_suite< AtomicStructureAdapter, false, atomadapter_indexing> @@ -406,7 +406,7 @@ class atomadapter_indexing : public vector_indexing_suite< // of any additional structure data. StructureAdapterPtr rv = container.clone(); AtomicStructureAdapterPtr rva; - rva = boost::static_pointer_cast(rv); + rva = std::static_pointer_cast(rv); // handle index ranges for a valid and empty slice if (from <= to) { @@ -458,7 +458,7 @@ void atomadapter_reserve(AtomicStructureAdapter& adpt, int sz) // Wrapper helpers for class PeriodicStructureAdapter typedef MakeWrapper PeriodicStructureAdapterWrap; -typedef boost::shared_ptr PeriodicStructureAdapterWrapPtr; +typedef std::shared_ptr PeriodicStructureAdapterWrapPtr; python::tuple periodicadapter_getlatpar(const PeriodicStructureAdapter& adpt) { @@ -471,7 +471,7 @@ python::tuple periodicadapter_getlatpar(const PeriodicStructureAdapter& adpt) // Wrapper helpers for class CrystalStructureAdapter typedef MakeWrapper CrystalStructureAdapterWrap; -typedef boost::shared_ptr CrystalStructureAdapterWrapPtr; +typedef std::shared_ptr CrystalStructureAdapterWrapPtr; double crystaladapter_getsymmetryprecision(const CrystalStructureAdapter& adpt) diff --git a/src/extensions/wrap_StructureAdapter.cpp b/src/extensions/wrap_StructureAdapter.cpp index 7a1a60c5..170737f0 100644 --- a/src/extensions/wrap_StructureAdapter.cpp +++ b/src/extensions/wrap_StructureAdapter.cpp @@ -446,7 +446,7 @@ class StructureProxyPickleSuite : public boost::python::pickle_suite diffpy::srreal::StructureAdapterPtr adpt) { using namespace boost; - shared_ptr tadpt = dynamic_pointer_cast(adpt); + std::shared_ptr tadpt = std::dynamic_pointer_cast(adpt); StructureAdapterPtr srcadpt = tadpt->getSourceStructure(); python::tuple rv = python::make_tuple(srcadpt); return rv; @@ -540,7 +540,7 @@ void wrap_StructureAdapter() register_ptr_to_python(); implicitly_convertible(); - typedef boost::shared_ptr + typedef std::shared_ptr NoMetaStructureAdapterPtr; class_, NoMetaStructureAdapterPtr>( @@ -550,7 +550,7 @@ void wrap_StructureAdapter() .def_pickle(StructureProxyPickleSuite()) ; - typedef boost::shared_ptr + typedef std::shared_ptr NoSymmetryStructureAdapterPtr; class_, NoSymmetryStructureAdapterPtr>( From a77b086a5a749cbd6f5930951b11f0f8db458fd9 Mon Sep 17 00:00:00 2001 From: symscae Date: Wed, 24 Jun 2026 11:51:39 -0700 Subject: [PATCH 02/18] added nanobind source to build system --- pyproject.toml | 7 +++++- requirements/conda.txt | 1 + setup.py | 53 ++++++++++++++++++++++++++++++++++++++++-- 3 files changed, 58 insertions(+), 3 deletions(-) diff --git a/pyproject.toml b/pyproject.toml index 840710d8..894051c4 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -1,5 +1,10 @@ [build-system] -requires = ["setuptools>=62.0", "setuptools-git-versioning>=2.0", "numpy"] +requires = [ + "setuptools>=62.0", + "setuptools-git-versioning>=2.0", + "numpy", + "nanobind", +] build-backend = "setuptools.build_meta" [project] diff --git a/requirements/conda.txt b/requirements/conda.txt index 0f11dbec..2944258c 100644 --- a/requirements/conda.txt +++ b/requirements/conda.txt @@ -4,3 +4,4 @@ libobjcryst pyobjcryst diffpy.structure periodictable +nanobind diff --git a/setup.py b/setup.py index c8d12d65..3048a38b 100644 --- a/setup.py +++ b/setup.py @@ -14,6 +14,22 @@ import numpy from setuptools import Extension, setup +from setuptools.command.build_ext import build_ext + + +def get_nanobind_config(): + import nanobind + + package_dir = Path(nanobind.__file__).parent + return { + "include_dirs": [ + nanobind.include_dir(), + str(package_dir / "ext" / "robin_map" / "include"), + ], + "source": str(Path(nanobind.source_dir()) / "nb_combined.cpp"), + } + +nanobind_macros = [("NB_COMPACT_ASSERTIONS", None)] def get_boost_libraries(): @@ -91,10 +107,12 @@ def get_objcryst_libraries(): if os.name == "nt": compile_args = ["/std:c++23"] + nanobind_compile_args = ["/std:c++23"] macros = [("_USE_MATH_DEFINES", None)] extra_link_args = ["/FORCE:MULTIPLE"] else: compile_args = ["-std=c++23"] + nanobind_compile_args = ["-std=c++23", "-fno-strict-aliasing"] macros = [] extra_link_args = [] @@ -112,13 +130,44 @@ def get_objcryst_libraries(): } +class build_ext_with_nanobind(build_ext): + def build_extension(self, ext): + nanobind_source = getattr(ext, "nanobind_source", None) + if not nanobind_source: + super().build_extension(ext) + return + + original_extra_objects = list(ext.extra_objects or []) + try: + nanobind_objects = self.compiler.compile( + [nanobind_source], + output_dir=self.build_temp, + macros=(ext.define_macros or []) + nanobind_macros, + include_dirs=ext.include_dirs, + debug=self.debug, + extra_postargs=nanobind_compile_args, + depends=ext.depends, + ) + ext.extra_objects = original_extra_objects + nanobind_objects + super().build_extension(ext) + finally: + ext.extra_objects = original_extra_objects + + def create_extensions(): "Initialize Extension objects for the setup function." + nanobind_cfg = get_nanobind_config() ext = Extension( "diffpy.srreal.srreal_ext", glob.glob("src/extensions/*.cpp"), - **ext_kws, + **{ + **ext_kws, + "include_dirs": ( + ext_kws["include_dirs"] + nanobind_cfg["include_dirs"] + ), + }, ) + ext.nanobind_source = nanobind_cfg["source"] return [ext] @@ -130,4 +179,4 @@ def ext_modules(): if __name__ == "__main__": - setup(ext_modules=ext_modules()) + setup(cmdclass={"build_ext": build_ext_with_nanobind}, ext_modules=ext_modules()) From 107ea7cf7e291153bf119faf0e0fee7be6847200 Mon Sep 17 00:00:00 2001 From: symscae Date: Wed, 24 Jun 2026 11:56:34 -0700 Subject: [PATCH 03/18] ran docformatter --- setup.py | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/setup.py b/setup.py index 3048a38b..a254f786 100644 --- a/setup.py +++ b/setup.py @@ -29,6 +29,7 @@ def get_nanobind_config(): "source": str(Path(nanobind.source_dir()) / "nb_combined.cpp"), } + nanobind_macros = [("NB_COMPACT_ASSERTIONS", None)] @@ -179,4 +180,7 @@ def ext_modules(): if __name__ == "__main__": - setup(cmdclass={"build_ext": build_ext_with_nanobind}, ext_modules=ext_modules()) + setup( + cmdclass={"build_ext": build_ext_with_nanobind}, + ext_modules=ext_modules(), + ) From 9439644af05ffc1e8b3d28eddc389ddfb4b9612a Mon Sep 17 00:00:00 2001 From: symscae Date: Wed, 24 Jun 2026 13:59:38 -0700 Subject: [PATCH 04/18] migrated syntax for srreal_converters and srreal_validators --- src/extensions/srreal_converters.cpp | 267 ++++++++++++++++----------- src/extensions/srreal_converters.hpp | 172 ++++++++--------- src/extensions/srreal_validators.cpp | 18 +- src/extensions/srreal_validators.hpp | 6 +- 4 files changed, 242 insertions(+), 221 deletions(-) diff --git a/src/extensions/srreal_converters.cpp b/src/extensions/srreal_converters.cpp index 298663d5..fd1390f4 100644 --- a/src/extensions/srreal_converters.cpp +++ b/src/extensions/srreal_converters.cpp @@ -17,15 +17,13 @@ * *****************************************************************************/ -#include -#include -#include +// TODO: replace Py_DECREF with nb::steal #include -#include #include #include #include +#include #include #include @@ -60,18 +58,26 @@ void translate_invalid_argument(const invalid_argument& e) } -boost::python::object newNumPyArray(int dim, const int* sz, int typenum) +nb::object newNumPyArray(int dim, const int* sz, int typenum) { - using namespace std; - using namespace boost; - // copy the size information to an array of npy_intp - valarray npsza(dim); - npy_intp& npsz = npsza[0]; - copy(sz, sz + dim, &npsz); - // create numpy array - python::object rv( - python::handle<>(PyArray_SimpleNew(dim, &npsz, typenum))); - return rv; + std::vector dims(static_cast(dim)); + std::transform( + sz, + sz + dim, + dims.begin(), + [](int v) { return static_cast(v); } + ); + + PyObject *arr = PyArray_SimpleNew( + dim, + dims.empty() ? nullptr : dims.data(), + typenum + ); + + if (!arr) + throw nb::python_error(); + + return nb::steal(arr); } } // namespace @@ -81,18 +87,31 @@ namespace srrealmodule { /// this function registers all exception translators void wrap_exceptions() { - using boost::python::register_exception_translator; - register_exception_translator( - &translate_DoubleAttributeError); - register_exception_translator( - &translate_invalid_argument); + nb::register_exception_translator( + [](const std::exception_ptr& p, void*) + { + try + { + if (p) + std::rethrow_exception(p); + } + catch (const DoubleAttributeError& e) + { + PyErr_SetString(PyExc_AttributeError, e.what()); + } + catch (const invalid_argument& e) + { + PyErr_SetString(PyExc_ValueError, e.what()); + } + } + ); } /// helper for creating numpy array of doubles NumPyArray_DoublePtr createNumPyDoubleArray(int dim, const int* sz) { - boost::python::object rvobj = newNumPyArray(dim, sz, NPY_DOUBLE); + nb::object rvobj = newNumPyArray(dim, sz, NPY_DOUBLE); PyArrayObject* a = reinterpret_cast(rvobj.ptr()); double* rvdata = static_cast(PyArray_DATA(a)); NumPyArray_DoublePtr rv(rvobj, rvdata); @@ -101,15 +120,22 @@ NumPyArray_DoublePtr createNumPyDoubleArray(int dim, const int* sz) /// helper for creating numpy array of the same shape as the argument -NumPyArray_DoublePtr createNumPyDoubleArrayLike(boost::python::object& obj) +NumPyArray_DoublePtr createNumPyDoubleArrayLike(nb::object& obj) { assert(PyArray_Check(obj.ptr())); PyArrayObject* a = reinterpret_cast(obj.ptr()); // create numpy array - boost::python::object rvobj( - boost::python::handle<>( - PyArray_NewLikeArray( - a, NPY_CORDER, PyArray_DescrFromType(NPY_DOUBLE), 0))); + PyObject *pobj = PyArray_NewLikeArray( + a, + NPY_CORDER, + PyArray_DescrFromType(NPY_DOUBLE), + 0 + ); + + if (!pobj) + throw nb::python_error(); + + nb::object rvobj = nb::steal(pobj); PyArrayObject* a1 = reinterpret_cast(rvobj.ptr()); double* rvdata = static_cast(PyArray_DATA(a1)); NumPyArray_DoublePtr rv(rvobj, rvdata); @@ -118,23 +144,33 @@ NumPyArray_DoublePtr createNumPyDoubleArrayLike(boost::python::object& obj) /// helper for creating a numpy array view on a double array -boost::python::object +nb::object createNumPyDoubleView(double* data, int dim, const int* sz) { - using namespace std; - using namespace boost; - valarray npsza(dim); - npy_intp* npsz = &(npsza[0]); - copy(sz, sz + dim, npsz); - python::object rv( - python::handle<>( - PyArray_SimpleNewFromData(dim, npsz, NPY_DOUBLE, data))); - return rv; + std::vector dims(static_cast(dim)); + std::transform( + sz, + sz + dim, + dims.begin(), + [](int v) { return static_cast(v); } + ); + + PyObject* pobj = PyArray_SimpleNewFromData( + dim, + dims.data(), + NPY_DOUBLE, + data + ); + + if (!pobj) + throw nb::python_error(); + + return nb::steal(pobj); } /// NumPy array view specializations for R3::Vector -boost::python::object viewAsNumPyArray(::diffpy::srreal::R3::Vector& v) +nb::object viewAsNumPyArray(::diffpy::srreal::R3::Vector& v) { using namespace diffpy::srreal; double* data = &(v[0]); @@ -144,7 +180,7 @@ boost::python::object viewAsNumPyArray(::diffpy::srreal::R3::Vector& v) /// NumPy array view specializations for R3::Matrix -boost::python::object viewAsNumPyArray(::diffpy::srreal::R3::Matrix& mx) +nb::object viewAsNumPyArray(::diffpy::srreal::R3::Matrix& mx) { using namespace diffpy::srreal; double* data = &(mx(0, 0)); @@ -155,9 +191,8 @@ boost::python::object viewAsNumPyArray(::diffpy::srreal::R3::Matrix& mx) /// Copy NumPy array to R3::Vector void assignR3Vector( - ::diffpy::srreal::R3::Vector& dst, boost::python::object& value) + ::diffpy::srreal::R3::Vector& dst, nb::object& value) { - using namespace boost; using diffpy::srreal::R3::Ndim; // If value is numpy array, try direct data access if (PyArray_Check(value.ptr())) @@ -167,30 +202,29 @@ void assignR3Vector( if (a && Ndim == PyArray_DIM(a, 0)) { double* p = static_cast(PyArray_DATA(a)); - std::copy(p, p + Ndim, dst.data().begin()); + std::ranges::copy(p, p + Ndim, dst.data().begin()); Py_DECREF(a); return; } Py_XDECREF(a); } // handle scalar assignment - python::extract getvalue(value); - if (getvalue.check()) + double scalar; + if (nb::try_cast(value, scalar)) { - std::fill(dst.data().begin(), dst.data().end(), getvalue()); + std::ranges::fill(dst.data().begin(), dst.data().end(), scalar); return; } // finally assign using array view - python::object dstview = viewAsNumPyArray(dst); - dstview[python::slice()] = value; + nb::object dstview = viewAsNumPyArray(dst); + dstview[nb::slice(nb::none(), nb::none(), nb::none())] = value; } /// Copy possible NumPy array to R3::Matrix void assignR3Matrix( - ::diffpy::srreal::R3::Matrix& dst, boost::python::object& value) + ::diffpy::srreal::R3::Matrix& dst, nb::object& value) { - using namespace boost; using diffpy::srreal::R3::Ndim; // If value is numpy array, try direct data access if (PyArray_Check(value.ptr())) @@ -200,29 +234,29 @@ void assignR3Matrix( if (a && Ndim == PyArray_DIM(a, 0) && Ndim == PyArray_DIM(a, 1)) { double* p = static_cast(PyArray_DATA(a)); - std::copy(p, p + Ndim * Ndim, dst.data().begin()); + std::ranges::copy(p, p + Ndim * Ndim, dst.data().begin()); Py_DECREF(a); return; } Py_XDECREF(a); } // handle scalar assignment - python::extract getvalue(value); - if (getvalue.check()) + double scalar; + if (nb::try_cast(value, scalar)) { - std::fill(dst.data().begin(), dst.data().end(), getvalue()); + std::ranges::fill(dst.data().begin(), dst.data().end(), scalar); return; } // finally assign using array view - python::object dstview = viewAsNumPyArray(dst); - dstview[python::slice()] = value; + nb::object dstview = viewAsNumPyArray(dst); + dstview[nb::slice(nb::none(), nb::none(), nb::none())] = value; } /// helper for creating numpy array of integers NumPyArray_IntPtr createNumPyIntArray(int dim, const int* sz) { - boost::python::object rvobj = newNumPyArray(dim, sz, NPY_INT); + nb::object rvobj = newNumPyArray(dim, sz, NPY_INT); PyArrayObject* a = reinterpret_cast(rvobj.ptr()); int* rvdata = static_cast(PyArray_DATA(a)); NumPyArray_IntPtr rv(rvobj, rvdata); @@ -233,14 +267,14 @@ NumPyArray_IntPtr createNumPyIntArray(int dim, const int* sz) /// efficient conversion of Python object to a QuantityType diffpy::srreal::QuantityType& extractQuantityType( - boost::python::object obj, + nb::object obj, diffpy::srreal::QuantityType& rv) { - using namespace boost; using diffpy::srreal::QuantityType; // extract QuantityType directly - python::extract getqt(obj); - if (getqt.check()) return getqt(); + QuantityType* qt = nullptr; + if (nb::try_cast(obj, qt) && qt) + return *qt; // copy data directly if it is a numpy array of doubles PyArrayObject* a = PyArray_Check(obj.ptr()) ? reinterpret_cast(obj.ptr()) : NULL; @@ -257,24 +291,38 @@ extractQuantityType( return rv; } // otherwise copy elementwise converting each element to a double - python::stl_input_iterator begin(obj), end; - rv.assign(begin, end); + std::vector tmp; + + PyObject* iter = PyObject_GetIter(obj.ptr()); + if (!iter) + throw nb::python_error(); + + nb::object it = nb::steal(iter); + + while (PyObject* item = PyIter_Next(it.ptr())) + { + nb::object item_obj = nb::steal(item); + tmp.push_back(nb::cast(item_obj)); + } + + if (PyErr_Occurred()) + throw nb::python_error(); + + rv.assign(tmp.begin(), tmp.end()); return rv; } /// efficient conversion of Python object to a numpy array of doubles -NumPyArray_DoublePtr extractNumPyDoubleArray(::boost::python::object& obj) +NumPyArray_DoublePtr extractNumPyDoubleArray(::nb::object& obj) { PyObject* pobj = PyArray_ContiguousFromAny(obj.ptr(), NPY_DOUBLE, 0, 0); if (!pobj) { - const char* emsg = "Cannot convert this object to numpy array."; - PyErr_SetString(PyExc_TypeError, emsg); - boost::python::throw_error_already_set(); - abort(); + PyErr_Clear(); + throw nb::type_error("Cannot convert this object to numpy array."); } - boost::python::object rvobj((boost::python::handle<>(pobj))); + nb::object rvobj = nb::steal(pobj); PyArrayObject* a = reinterpret_cast(pobj); double* rvdata = static_cast(PyArray_DATA(a)); NumPyArray_DoublePtr rv(rvobj, rvdata); @@ -283,46 +331,55 @@ NumPyArray_DoublePtr extractNumPyDoubleArray(::boost::python::object& obj) /// extract double with a support for numpy.int types -double extractdouble(boost::python::object obj) +double extractdouble(nb::object obj) { - using namespace boost; - python::extract getx(obj); - if (getx.check()) return getx(); + double x; + if (nb::try_cast(obj, x)) + return x; + PyObject* pobj = obj.ptr(); if (PyArray_CheckScalar(pobj)) { - double x; - PyArray_CastScalarToCtype(pobj, &x, - PyArray_DescrFromType(NPY_DOUBLE)); + PyArray_Descr* descr = PyArray_DescrFromType(NPY_DOUBLE); + if (!descr) + throw nb::python_error(); + + int ok = PyArray_CastScalarToCtype(pobj, &x, descr); + Py_DECREF(descr); + + if (ok < 0) + throw nb::python_error(); + return x; } - // nothing worked, call getx which will raise an exception - return getx(); + // nothing worked, call default behavior which will raise an exception + return nb::cast(obj); } /// extract integer with a support for numpy.int types -int extractint(boost::python::object obj) +int extractint(nb::object obj) { - using namespace boost; - python::extract geti(obj); - if (geti.check()) return geti(); + int i; + if (nb::try_cast(obj, i)) + return i; + PyObject* pobj = obj.ptr(); if (PyArray_CheckScalar(pobj)) { int rv = PyArray_PyIntAsInt(pobj); - if (rv == -1 && PyErr_Occurred()) python::throw_error_already_set(); + if (rv == -1 && PyErr_Occurred()) + throw nb::python_error(); return rv; } - // nothing worked, call geti which will raise an exception - return geti(); + // nothing worked, call default behavior which will raise an exception + return nb::cast(obj); } /// extract a vector of integers from a numpy array, iterable or scalar -std::vector extractintvector(boost::python::object obj) +std::vector extractintvector(nb::object obj) { - using namespace boost::python; std::vector rv; // iterable of integers if (isiterable(obj)) @@ -334,11 +391,14 @@ std::vector extractintvector(boost::python::object obj) a && (1 == PyArray_NDIM(a)) && PyArray_ISINTEGER(a); if (isintegernumpyarray) { - object aobj = obj; + nb::object aobj = obj; if (NPY_INT != PyArray_TYPE(a)) { - object a1(handle<>(PyArray_Cast(a, NPY_INT))); - aobj = a1; + PyObject* casted = PyArray_Cast(a, NPY_INT); + if (!casted) + throw nb::python_error(); + + aobj = nb::steal(casted); } PyArrayObject* a1 = reinterpret_cast(aobj.ptr()); assert(NPY_INT == PyArray_TYPE(a1)); @@ -348,13 +408,11 @@ std::vector extractintvector(boost::python::object obj) return rv; } // otherwise translate every item in the iterable - stl_input_iterator ii(obj), end; rv.reserve(len(obj)); - for (; ii != end; ++ii) - { - int idx = extractint(*ii); - rv.push_back(idx); - } + + for (nb::handle item : obj) + rv.push_back(extractint(nb::borrow(item))); + return rv; } // try to handle it as a scalar @@ -370,8 +428,7 @@ void throwPureVirtualCalled(const char* fncname) std::string emsg = "Pure virtual function '"; emsg += fncname; emsg += "' called."; - PyErr_SetString(PyExc_RuntimeError, emsg.c_str()); - boost::python::throw_error_already_set(); + throw std::runtime_error(emsg); } } // namespace srrealmodule @@ -382,19 +439,15 @@ namespace srreal { /// shared converter that first tries to extract the pointer and then calls /// diffpy.srreal.structureadapter.createStructureAdapter -StructureAdapterPtr createStructureAdapter(::boost::python::object stru) +StructureAdapterPtr createStructureAdapter(nb::object stru) { - using namespace boost::python; StructureAdapterPtr adpt; - extract getadpt(stru); - if (getadpt.check()) adpt = getadpt(); - else - { - object mod = import("diffpy.srreal.structureadapter"); - object convertinpython = mod.attr("createStructureAdapter"); - adpt = extract(convertinpython(stru)); - } - return adpt; + if (nb::try_cast(stru, adpt)) + return adpt; + + nb::object mod = nb::module_::import_("diffpy.srreal.structureadapter"); + nb::object convertinpython = mod.attr("createStructureAdapter"); + return nb::cast(convertinpython(stru)); } } // namespace srreal diff --git a/src/extensions/srreal_converters.hpp b/src/extensions/srreal_converters.hpp index b888c001..fb3cad6e 100644 --- a/src/extensions/srreal_converters.hpp +++ b/src/extensions/srreal_converters.hpp @@ -20,11 +20,7 @@ #ifndef SRREAL_CONVERTERS_HPP_INCLUDED #define SRREAL_CONVERTERS_HPP_INCLUDED -#include -#include -#include -#include -#include +#include #include #include @@ -38,6 +34,8 @@ #error diffpy.srreal requires libdiffpy 1.4.0 or later. #endif +namespace nb = nanobind; + /// Conversion function that supports implicit conversions in /// PairQuantity::eval and PairQuantity::setStructure @@ -45,7 +43,7 @@ namespace diffpy { namespace srreal { StructureAdapterPtr -createStructureAdapter(::boost::python::object); +createStructureAdapter(nb::object); } // namespace srreal } // namespace diffpy @@ -58,16 +56,13 @@ using diffpy::srreal::createStructureAdapter; /// either instance or a type string #define DECLARE_BYTYPE_SETTER_WRAPPER(method, wrapper) \ template \ - void wrapper(T& obj, ::boost::python::object value) \ + void wrapper(T& obj, nb::object value) \ { \ - using ::boost::python::extract; \ - extract tp(value); \ - if (tp.check()) obj.method##ByType(tp()); \ - else \ - { \ - typename V::SharedPtr p = extract(value); \ - obj.method(p); \ - } \ + std::string tp; \ + if (nb::try_cast(value, tp)) \ + obj.method##ByType(tp); \ + else \ + obj.method(nb::cast(value)); \ } \ @@ -75,9 +70,9 @@ using diffpy::srreal::createStructureAdapter; /// that converts the result to numpy array #define DECLARE_PYARRAY_METHOD_WRAPPER(method, wrapper) \ template \ - ::boost::python::object wrapper(const T& obj) \ + nb::object wrapper(const T& obj) \ { \ - ::boost::python::object rv = convertToNumPyArray(obj.method()); \ + nb::object rv = convertToNumPyArray(obj.method()); \ return rv; \ } \ @@ -86,9 +81,9 @@ using diffpy::srreal::createStructureAdapter; /// that converts the result to numpy array #define DECLARE_PYARRAY_METHOD_WRAPPER1(method, wrapper) \ template \ - ::boost::python::object wrapper(const T& obj, const T1& a1) \ + nb::object wrapper(const T& obj, const T1& a1) \ { \ - ::boost::python::object rv = convertToNumPyArray(obj.method(a1)); \ + nb::object rv = convertToNumPyArray(obj.method(a1)); \ return rv; \ } \ @@ -97,12 +92,12 @@ using diffpy::srreal::createStructureAdapter; /// that converts the result to numpy character array #define DECLARE_PYCHARARRAY_METHOD_WRAPPER(method, wrapper) \ template \ - ::boost::python::object wrapper(const T& obj) \ + nb::object wrapper(const T& obj) \ { \ - ::boost::python::list lst = convertToPythonList(obj.method()); \ - ::boost::python::object tochararray = \ - ::boost::python::import("numpy").attr("char").attr("array"); \ - ::boost::python::object rv = tochararray(lst); \ + nb::list lst = convertToPythonList(obj.method()); \ + nb::object tochararray = \ + nb::module_::import_("numpy").attr("char").attr("array"); \ + nb::object rv = tochararray(lst); \ return rv; \ } \ @@ -111,9 +106,9 @@ using diffpy::srreal::createStructureAdapter; /// that converts the result to a python set #define DECLARE_PYSET_METHOD_WRAPPER(method, wrapper) \ template \ - ::boost::python::object wrapper(const T& obj) \ + nb::object wrapper(const T& obj) \ { \ - ::boost::python::object rv = convertToPythonSet(obj.method()); \ + nb::object rv = convertToPythonSet(obj.method()); \ return rv; \ } \ @@ -122,9 +117,9 @@ using diffpy::srreal::createStructureAdapter; /// that converts the result to a python set #define DECLARE_PYSET_METHOD_WRAPPER1(method, wrapper) \ template \ - ::boost::python::object wrapper(const T& obj, const T1& a1) \ + nb::object wrapper(const T& obj, const T1& a1) \ { \ - ::boost::python::object rv = convertToPythonSet(obj.method(a1)); \ + nb::object rv = convertToPythonSet(obj.method(a1)); \ return rv; \ } \ @@ -132,9 +127,9 @@ using diffpy::srreal::createStructureAdapter; /// this macro defines a wrapper for C++ function without arguments /// that converts the result to a python set #define DECLARE_PYSET_FUNCTION_WRAPPER(fnc, wrapper) \ - ::boost::python::object wrapper() \ + nb::object wrapper() \ { \ - ::boost::python::object rv = convertToPythonSet(fnc()); \ + nb::object rv = convertToPythonSet(fnc()); \ return rv; \ } \ @@ -143,9 +138,9 @@ using diffpy::srreal::createStructureAdapter; /// that converts the result to a python list #define DECLARE_PYLIST_METHOD_WRAPPER(method, wrapper) \ template \ - ::boost::python::object wrapper(const T& obj) \ + nb::object wrapper(const T& obj) \ { \ - ::boost::python::object rv = convertToPythonList(obj.method()); \ + nb::object rv = convertToPythonList(obj.method()); \ return rv; \ } \ @@ -154,9 +149,9 @@ using diffpy::srreal::createStructureAdapter; /// that convert the result to python list #define DECLARE_PYLIST_METHOD_WRAPPER1(method, wrapper) \ template \ - ::boost::python::object wrapper(const T& obj, const T1& a1) \ + nb::object wrapper(const T& obj, const T1& a1) \ { \ - ::boost::python::object rv = convertToPythonList(obj.method(a1)); \ + nb::object rv = convertToPythonList(obj.method(a1)); \ return rv; \ } \ @@ -165,9 +160,9 @@ using diffpy::srreal::createStructureAdapter; /// that converts the result to a python list of NumPy arrays #define DECLARE_PYLISTARRAY_METHOD_WRAPPER(method, wrapper) \ template \ - ::boost::python::list wrapper(const T& obj) \ + nb::list wrapper(const T& obj) \ { \ - ::boost::python::list rvlist; \ + nb::list rvlist; \ fillPyListWithArrays(rvlist, obj.method()); \ return rvlist; \ } \ @@ -177,9 +172,9 @@ using diffpy::srreal::createStructureAdapter; /// that converts the result to a list of Python sets #define DECLARE_PYLISTSET_METHOD_WRAPPER(method, wrapper) \ template \ - ::boost::python::list wrapper(const T& obj) \ + nb::list wrapper(const T& obj) \ { \ - ::boost::python::list rvlist; \ + nb::list rvlist; \ fillPyListWithSets(rvlist, obj.method()); \ return rvlist; \ } \ @@ -189,9 +184,9 @@ using diffpy::srreal::createStructureAdapter; /// that converts the result to a python dict #define DECLARE_PYDICT_METHOD_WRAPPER(method, wrapper) \ template \ - ::boost::python::object wrapper(const T& obj) \ + nb::object wrapper(const T& obj) \ { \ - ::boost::python::object rv = convertToPythonDict(obj.method()); \ + nb::object rv = convertToPythonDict(obj.method()); \ return rv; \ } \ @@ -200,16 +195,16 @@ using diffpy::srreal::createStructureAdapter; /// that converts the result to a python dict #define DECLARE_PYDICT_METHOD_WRAPPER1(method, wrapper) \ template \ - ::boost::python::object wrapper(const T& obj, const T1& a1) \ + nb::object wrapper(const T& obj, const T1& a1) \ { \ - ::boost::python::object rv = convertToPythonDict(obj.method(a1)); \ + nb::object rv = convertToPythonDict(obj.method(a1)); \ return rv; \ } \ /// helper template function for DECLARE_PYLISTARRAY_METHOD_WRAPPER template -void fillPyListWithArrays(::boost::python::list lst, const T& value) +void fillPyListWithArrays(nb::list lst, const T& value) { typename T::const_iterator v = value.begin(); for (; v != value.end(); ++v) lst.append(convertToNumPyArray(*v)); @@ -218,21 +213,19 @@ void fillPyListWithArrays(::boost::python::list lst, const T& value) /// template function for converting C++ STL container to a python set template -::boost::python::object +nb::object convertToPythonSet(const T& value) { - using namespace ::boost; - python::object rvset(python::handle<>(PySet_New(NULL))); - python::object rvset_add = rvset.attr("add"); - typename T::const_iterator ii; - for (ii = value.begin(); ii != value.end(); ++ii) rvset_add(*ii); - return rvset; + nb::set rv; + for (auto const& item : value) + rv.add(item); + return rv; } /// helper template function for DECLARE_PYLISTSET_METHOD_WRAPPER template -void fillPyListWithSets(::boost::python::list lst, const T& value) +void fillPyListWithSets(nb::list lst, const T& value) { typename T::const_iterator v = value.begin(); for (; v != value.end(); ++v) lst.append(convertToPythonSet(*v)); @@ -240,7 +233,7 @@ void fillPyListWithSets(::boost::python::list lst, const T& value) /// Type for numpy array object and a raw pointer to its double data -typedef std::pair NumPyArray_DoublePtr; +typedef std::pair NumPyArray_DoublePtr; /// helper for creating numpy array of doubles @@ -248,16 +241,16 @@ NumPyArray_DoublePtr createNumPyDoubleArray(int dim, const int* sz); /// helper for creating numpy array of the same shape as the argument -NumPyArray_DoublePtr createNumPyDoubleArrayLike(boost::python::object& obj); +NumPyArray_DoublePtr createNumPyDoubleArrayLike(nb::object& obj); /// helper for creating numpy views on existing double array -boost::python::object createNumPyDoubleView(double*, int dim, const int* sz); +nb::object createNumPyDoubleView(double*, int dim, const int* sz); /// template function for converting iterables to numpy array of doubles template -::boost::python::object +nb::object convertToNumPyArray(Iter first, Iter last) { int sz = last - first; @@ -268,7 +261,7 @@ convertToNumPyArray(Iter first, Iter last) /// specialization for R3::Vector -inline ::boost::python::object +inline nb::object convertToNumPyArray(const ::diffpy::srreal::R3::Vector& value) { return convertToNumPyArray(value.begin(), value.end()); @@ -276,7 +269,7 @@ convertToNumPyArray(const ::diffpy::srreal::R3::Vector& value) /// specialization for R3::Matrix -inline ::boost::python::object +inline nb::object convertToNumPyArray(const ::diffpy::srreal::R3::Matrix& mx) { using namespace diffpy::srreal; @@ -291,7 +284,7 @@ convertToNumPyArray(const ::diffpy::srreal::R3::Matrix& mx) /// specialization for std::vector -inline ::boost::python::object +inline nb::object convertToNumPyArray(const ::std::vector& vr3v) { using namespace diffpy::srreal; @@ -312,7 +305,7 @@ convertToNumPyArray(const ::std::vector& vr3v) /// specialization for QuantityType -inline ::boost::python::object +inline nb::object convertToNumPyArray(const ::diffpy::srreal::QuantityType& value) { return convertToNumPyArray(value.begin(), value.end()); @@ -320,27 +313,27 @@ convertToNumPyArray(const ::diffpy::srreal::QuantityType& value) /// NumPy array view specializations for R3::Vector -boost::python::object +nb::object viewAsNumPyArray(::diffpy::srreal::R3::Vector&); /// NumPy array view specializations for R3::Matrix -boost::python::object +nb::object viewAsNumPyArray(::diffpy::srreal::R3::Matrix&); /// Copy possible NumPy array to R3::Vector void assignR3Vector( - ::diffpy::srreal::R3::Vector& dst, boost::python::object& value); + ::diffpy::srreal::R3::Vector& dst, nb::object& value); /// Copy possible NumPy array to R3::Matrix void assignR3Matrix( - ::diffpy::srreal::R3::Matrix& dst, boost::python::object& value); + ::diffpy::srreal::R3::Matrix& dst, nb::object& value); /// Type for numpy array object and a raw pointer to its double data -typedef std::pair NumPyArray_IntPtr; +typedef std::pair NumPyArray_IntPtr; /// helper for creating numpy array of integers @@ -348,7 +341,7 @@ NumPyArray_IntPtr createNumPyIntArray(int dim, const int* sz); /// specialization for a vector of integers -inline ::boost::python::object +inline nb::object convertToNumPyArray(const ::std::vector& value) { int sz = value.size(); @@ -360,25 +353,24 @@ convertToNumPyArray(const ::std::vector& value) /// template function for converting C++ STL container to a python list template -::boost::python::list +nb::list convertToPythonList(const T& value) { - using namespace ::boost; - python::list rvlist; - typename T::const_iterator ii; - for (ii = value.begin(); ii != value.end(); ++ii) rvlist.append(*ii); - return rvlist; + nb::list rv; + for (auto const& item : value) + rv.append(item); + return rv; } /// template converter of a C++ map-like container to a python dictionary template -::boost::python::dict +nb::dict convertToPythonDict(const T& value) { - ::boost::python::dict rv; - typename T::const_iterator ii = value.begin(); - for (; ii != value.end(); ++ii) rv[ii->first] = ii->second; + nb::dict rv; + for (auto const& item : value) + rv[item.first] = item.second; return rv; } @@ -387,49 +379,29 @@ convertToPythonDict(const T& value) /// If obj wraps a QuantityType reference, return that reference. /// Otherwise copy the obj values to rv and return rv. ::diffpy::srreal::QuantityType& -extractQuantityType(::boost::python::object obj, +extractQuantityType(nb::object obj, ::diffpy::srreal::QuantityType& rv); /// efficient conversion of Python object to a numpy array of doubles -NumPyArray_DoublePtr extractNumPyDoubleArray(::boost::python::object& obj); +NumPyArray_DoublePtr extractNumPyDoubleArray(nb::object& obj); /// extract double with a support for numpy numeric types -double extractdouble(::boost::python::object obj); +double extractdouble(nb::object obj); /// extract integer with a support for numpy.int types -int extractint(::boost::python::object obj); +int extractint(nb::object obj); /// extract a vector of integers from a numpy array, iterable or scalar -std::vector extractintvector(::boost::python::object obj); +std::vector extractintvector(nb::object obj); /// helper for raising RuntimeError on a call of pure virtual function void throwPureVirtualCalled(const char* fncname); - -/// template class for getting overrides to pure virtual method -template -class wrapper_srreal : public ::boost::python::wrapper -{ - public: - - typedef T base; - - protected: - - ::boost::python::override - get_pure_virtual_override(const char* name) const - { - ::boost::python::override f = this->get_override(name); - if (!f) throwPureVirtualCalled(name); - return f; - } -}; - } // namespace srrealmodule // Include shared wrapper definitions ---------------------------------------- diff --git a/src/extensions/srreal_validators.cpp b/src/extensions/srreal_validators.cpp index 4a3cb217..bb42bd24 100644 --- a/src/extensions/srreal_validators.cpp +++ b/src/extensions/srreal_validators.cpp @@ -16,21 +16,15 @@ * *****************************************************************************/ -#include -#include - #include "srreal_validators.hpp" namespace srrealmodule { -using namespace boost::python; - void ensure_index_bounds(int idx, int lo, int hi) { if (idx < lo || idx >= hi) { - PyErr_SetString(PyExc_IndexError, "Index out of range."); - throw_error_already_set(); + throw nb::index_error("Index out of range."); } } @@ -39,19 +33,17 @@ void ensure_non_negative(int value) { if (value < 0) { - PyErr_SetString(PyExc_ValueError, "Value cannot be negative."); - throw_error_already_set(); + throw nb::value_error("Value cannot be negative."); } } -bool isiterable(boost::python::object obj) +bool isiterable(nb::object obj) { - using boost::python::import; #if PY_MAJOR_VERSION >= 3 - object Iterable = import("collections.abc").attr("Iterable"); + nb::object Iterable = nb::module_::import_("collections.abc").attr("Iterable"); #else - object Iterable = import("collections").attr("Iterable"); + nb::object Iterable = nb::module_::import_("collections").attr("Iterable"); #endif bool rv = (1 == PyObject_IsInstance(obj.ptr(), Iterable.ptr())); return rv; diff --git a/src/extensions/srreal_validators.hpp b/src/extensions/srreal_validators.hpp index f123498f..bb49fff9 100644 --- a/src/extensions/srreal_validators.hpp +++ b/src/extensions/srreal_validators.hpp @@ -19,11 +19,15 @@ #ifndef SRREAL_VALIDATORS_HPP_INCLUDED #define SRREAL_VALIDATORS_HPP_INCLUDED +#include + +namespace nb = nanobind; + namespace srrealmodule { void ensure_index_bounds(int idx, int lo, int hi); void ensure_non_negative(int value); -bool isiterable(boost::python::object obj); +bool isiterable(nb::object obj); } // namespace srrealmodule From 4b8d1deddb4b22c6c083b82cdfeda6e55a917de8 Mon Sep 17 00:00:00 2001 From: symscae Date: Wed, 24 Jun 2026 16:01:53 -0700 Subject: [PATCH 05/18] migrated syntax for srreal_ext and srreal_registry --- src/extensions/srreal_ext.cpp | 120 +++++++++++++++-------------- src/extensions/srreal_registry.cpp | 19 +++-- src/extensions/srreal_registry.hpp | 99 ++++++++++++------------ 3 files changed, 119 insertions(+), 119 deletions(-) diff --git a/src/extensions/srreal_ext.cpp b/src/extensions/srreal_ext.cpp index 0bd4dbf4..6a932aa1 100644 --- a/src/extensions/srreal_ext.cpp +++ b/src/extensions/srreal_ext.cpp @@ -16,39 +16,37 @@ * *****************************************************************************/ -#include -#include -#include -#include -#include -#include "srreal_numpy_symbol.hpp" +#include + #include // Declaration of the external wrappers -------------------------------------- +namespace nb = nanobind; + namespace srrealmodule { -void wrap_libdiffpy_version(); +void wrap_libdiffpy_version(nb::module_& m); void wrap_exceptions(); -void wrap_EventTicker(); -void wrap_Attributes(); -void wrap_StructureDifference(); -void wrap_StructureAdapter(); -void wrap_AtomicStructureAdapter(); -void wrap_ObjCrystAdapters(); -void wrap_BaseBondGenerator(); -void wrap_PairQuantity(); -void wrap_PeakWidthModel(); -void wrap_ScatteringFactorTable(); -void wrap_PeakProfile(); -void wrap_BVParametersTable(); -void wrap_BVSCalculator(); -void wrap_PDFBaseline(); -void wrap_PDFEnvelope(); -void wrap_PDFCalculators(); -void wrap_BondCalculator(); -void wrap_AtomRadiiTable(); -void wrap_OverlapCalculator(); +void wrap_EventTicker(nb::module_& m); +void wrap_Attributes(nb::module_& m); +void wrap_StructureDifference(nb::module_& m); +void wrap_StructureAdapter(nb::module_& m); +void wrap_AtomicStructureAdapter(nb::module_& m); +void wrap_ObjCrystAdapters(nb::module_& m); +void wrap_BaseBondGenerator(nb::module_& m); +void wrap_PairQuantity(nb::module_& m); +void wrap_PeakWidthModel(nb::module_& m); +void wrap_ScatteringFactorTable(nb::module_& m); +void wrap_PeakProfile(nb::module_& m); +void wrap_BVParametersTable(nb::module_& m); +void wrap_BVSCalculator(nb::module_& m); +void wrap_PDFBaseline(nb::module_& m); +void wrap_PDFEnvelope(nb::module_& m); +void wrap_PDFCalculators(nb::module_& m); +void wrap_BondCalculator(nb::module_& m); +void wrap_AtomRadiiTable(nb::module_& m); +void wrap_OverlapCalculator(nb::module_& m); } // namespace srrealmodule @@ -64,46 +62,52 @@ namespace { // Module Definitions -------------------------------------------------------- -BOOST_PYTHON_MODULE(srreal_ext) +NB_MODULE(srreal_ext, m) { using namespace srrealmodule; // initialize numpy module initialize_numpy(); // execute external wrappers - wrap_libdiffpy_version(); + wrap_libdiffpy_version(m); wrap_exceptions(); - wrap_EventTicker(); - wrap_Attributes(); - wrap_StructureDifference(); - wrap_StructureAdapter(); - wrap_AtomicStructureAdapter(); - wrap_ObjCrystAdapters(); - wrap_BaseBondGenerator(); - wrap_PairQuantity(); - wrap_PeakWidthModel(); - wrap_ScatteringFactorTable(); - wrap_PeakProfile(); - wrap_BVParametersTable(); - wrap_BVSCalculator(); - wrap_PDFBaseline(); - wrap_PDFEnvelope(); - wrap_PDFCalculators(); - wrap_BondCalculator(); - wrap_AtomRadiiTable(); - wrap_OverlapCalculator(); + wrap_EventTicker(m); + wrap_Attributes(m); + wrap_StructureDifference(m); + wrap_StructureAdapter(m); + wrap_AtomicStructureAdapter(m); + wrap_ObjCrystAdapters(m); + wrap_BaseBondGenerator(m); + wrap_PairQuantity(m); + wrap_PeakWidthModel(m); + wrap_ScatteringFactorTable(m); + wrap_PeakProfile(m); + wrap_BVParametersTable(m); + wrap_BVSCalculator(m); + wrap_PDFBaseline(m); + wrap_PDFEnvelope(m); + wrap_PDFCalculators(m); + wrap_BondCalculator(m); + wrap_AtomRadiiTable(m); + wrap_OverlapCalculator(m); // load Python modules that tweak the wrapped classes - using boost::python::object; - using boost::python::import; - using boost::python::scope; - using boost::python::extract; - using boost::python::dict; - object srreal = import("diffpy.srreal"); - if (!PyObject_HasAttrString(srreal.ptr(), "_final_imports")) + nb::module_ srreal = nb::module_::import_("diffpy.srreal"); + if (!nb::hasattr(srreal, "_final_imports")) { - dict sysmods = extract(import("sys").attr("modules")); - sysmods.setdefault("diffpy.srreal.srreal_ext", scope()); - object import_now = - import("diffpy.srreal._final_imports").attr("import_now"); + PyObject* sysmods = PyImport_GetModuleDict(); + + const char* fqname = "diffpy.srreal.srreal_ext"; + if (PyDict_GetItemString(sysmods, fqname) == nullptr) + { + if (PyDict_SetItemString(sysmods, fqname, m.ptr()) < 0) + { + nb::raise_python_error(); + } + } + + nb::module_ final_imports = + nb::module_::import_("diffpy.srreal._final_imports"); + + nb::object import_now = final_imports.attr("import_now"); import_now(); } } diff --git a/src/extensions/srreal_registry.cpp b/src/extensions/srreal_registry.cpp index 6a4df0c4..c6ec1241 100644 --- a/src/extensions/srreal_registry.cpp +++ b/src/extensions/srreal_registry.cpp @@ -16,27 +16,26 @@ * *****************************************************************************/ -#include -#include +#include -namespace srrealmodule { +namespace nb = nanobind; -using namespace boost::python; +namespace srrealmodule { void register_for_cleanup(PyObject* pobj) { - object obj(borrowed(pobj)); - object reg = import("diffpy.srreal._cleanup").attr("registerForCleanUp"); + nb::object obj = nb::borrow(pobj); + nb::object reg = nb::module_::import_("diffpy.srreal._cleanup").attr("registerForCleanUp"); reg(obj); } /// get dictionary of Python-defined docstrings for the cls class. -object get_registry_docstrings(object& cls) +nb::object get_registry_docstrings(nb::object& cls) { - object mod = import("diffpy.srreal._docstrings"); - object getdocs = mod.attr("get_registry_docstrings"); - object rv = getdocs(cls); + nb::object mod = nb::module_::import_("diffpy.srreal._docstrings"); + nb::object getdocs = mod.attr("get_registry_docstrings"); + nb::object rv = getdocs(cls); return rv; } diff --git a/src/extensions/srreal_registry.hpp b/src/extensions/srreal_registry.hpp index 7df825d9..c08bfc73 100644 --- a/src/extensions/srreal_registry.hpp +++ b/src/extensions/srreal_registry.hpp @@ -19,8 +19,9 @@ #ifndef SRREAL_REGISTRY_HPP_INCLUDED #define SRREAL_REGISTRY_HPP_INCLUDED -#include -#include +#include + +namespace nb = nanobind; namespace srrealmodule { @@ -44,9 +45,9 @@ class wrapper_registry_configurator /// the fetch method should be called only from the wrapped method /// create() to remember pointers to the last Python object and /// the C++ instance that it wraps. - TSharedPtr fetch(::boost::python::object& obj) const + TSharedPtr fetch(nb::object& obj) const { - TSharedPtr p = ::boost::python::extract(obj); + TSharedPtr p = nb::cast(obj); mcptr = p.get(); mpyptr = obj.ptr(); return p; @@ -73,73 +74,69 @@ class wrapper_registry_configurator /// retrieve a dictionary of Python-defined docstrings for the cls class. -::boost::python::object get_registry_docstrings(::boost::python::object& cls); +nb::object get_registry_docstrings(nb::object& cls); /// helper wrapper functions for return value conversions. template -::boost::python::object getAliasedTypes_asdict() +nb::object getAliasedTypes_asdict() { return convertToPythonDict(W::getAliasedTypes()); } template -::boost::python::object getRegisteredTypes_asset() +nb::object getRegisteredTypes_asset() { return convertToPythonSet(W::getRegisteredTypes()); } /// template function that wraps HasClassRegistry methods -template -C& wrap_registry_methods(C& boostpythonclass) +template +nb::class_& wrap_registry_methods(nb::class_& cls) { - namespace bp = boost::python; - using namespace boost::python; - typedef typename C::wrapped_type::base B; - typedef extract CString; // get docstrings for the class registry methods. - object d = get_registry_docstrings(boostpythonclass); - const char* doc_create = CString(d["create"]); - const char* doc_clone = CString(d["clone"]); - const char* doc_type = CString(d["type"]); - const char* doc__registerThisType = CString(d["_registerThisType"]); - const char* doc__aliasType = CString(d["_aliasType"]); - const char* doc__deregisterType = CString(d["_deregisterType"]); - const char* doc_createByType = CString(d["createByType"]); - const char* doc_isRegisteredType = CString(d["isRegisteredType"]); - const char* doc_getAliasedTypes = CString(d["getAliasedTypes"]); - const char* doc_getRegisteredTypes = CString(d["getRegisteredTypes"]); + nb::object d = get_registry_docstrings(cls); + std::string doc_create = nb::cast(d["create"]); + std::string doc_clone = nb::cast(d["clone"]); + std::string doc_type = nb::cast(d["type"]); + std::string doc__registerThisType = nb::cast(d["_registerThisType"]); + std::string doc__aliasType = nb::cast(d["_aliasType"]); + std::string doc__deregisterType = nb::cast(d["_deregisterType"]); + std::string doc_createByType = nb::cast(d["createByType"]); + std::string doc_isRegisteredType = nb::cast(d["isRegisteredType"]); + std::string doc_getAliasedTypes = nb::cast(d["getAliasedTypes"]); + std::string doc_getRegisteredTypes = nb::cast(d["getRegisteredTypes"]); // define the class registry related methods. - boostpythonclass - .def("create", &B::create, doc_create) - .def("clone", &B::clone, doc_clone) - .def("type", &B::type, - return_value_policy(), - doc_type) - .def("_registerThisType", &B::registerThisType, - doc__registerThisType) - .def("_aliasType", &B::aliasType, - (bp::arg("tp"), bp::arg("alias")), doc__aliasType) - .staticmethod("_aliasType") - .def("_deregisterType", &B::deregisterType, - bp::arg("tp"), doc__deregisterType) - .staticmethod("_deregisterType") - .def("createByType", &B::createByType, - bp::arg("tp"), doc_createByType) - .staticmethod("createByType") - .def("isRegisteredType", &B::isRegisteredType, - bp::arg("tp"), doc_isRegisteredType) - .staticmethod("isRegisteredType") - .def("getAliasedTypes", getAliasedTypes_asdict, - doc_getAliasedTypes) - .staticmethod("getAliasedTypes") - .def("getRegisteredTypes", getRegisteredTypes_asset, - doc_getRegisteredTypes) - .staticmethod("getRegisteredTypes") + cls + .def("create", &C::create, + doc_create.c_str()) + .def("clone", &C::clone, + doc_clone.c_str()) + .def("type", &C::type, + nb::rv_policy::copy, + doc_type.c_str()) + .def("_registerThisType", &C::registerThisType, + doc__registerThisType.c_str()) + .def_static("_aliasType", &C::aliasType, + nb::arg("tp"), nb::arg("alias"), + doc__aliasType.c_str()) + .def_static("_deregisterType", &C::deregisterType, + nb::arg("tp"), + doc__deregisterType.c_str()) + .def_static("createByType", &C::createByType, + nb::arg("tp"), + doc_createByType.c_str()) + .def_static("isRegisteredType", &C::isRegisteredType, + nb::arg("tp"), + doc_isRegisteredType.c_str()) + .def_static("getAliasedTypes", &getAliasedTypes_asdict, + doc_getAliasedTypes.c_str()) + .def_static("getRegisteredTypes", &getRegisteredTypes_asset, + doc_getRegisteredTypes.c_str()) ; - return boostpythonclass; + return cls; } From 52849d3eb0308d259f277850321e35ff0ffd4fa9 Mon Sep 17 00:00:00 2001 From: symscae Date: Wed, 24 Jun 2026 21:03:47 -0700 Subject: [PATCH 06/18] reworked pickling and migrated syntax for wrap_AtomicStructureAdapter --- src/extensions/srreal_pickling.cpp | 12 - src/extensions/srreal_pickling.hpp | 248 ++++++++----- .../wrap_AtomicStructureAdapter.cpp | 343 +++++++++++------- 3 files changed, 364 insertions(+), 239 deletions(-) diff --git a/src/extensions/srreal_pickling.cpp b/src/extensions/srreal_pickling.cpp index 39dff13e..8514e969 100644 --- a/src/extensions/srreal_pickling.cpp +++ b/src/extensions/srreal_pickling.cpp @@ -21,9 +21,7 @@ #include namespace srrealmodule { -namespace { -using diffpy::srreal::StructureAdapterPtr; StructureAdapterPtr createStructureAdapterFromString(const std::string& content) @@ -33,16 +31,6 @@ createStructureAdapterFromString(const std::string& content) return adpt; } -} // namespace - -// Non-member functions for StructureAdapterPickleSuite ---------------------- - -boost::python::object -StructureAdapter_constructor() -{ - return boost::python::make_constructor(createStructureAdapterFromString); -} - } // namespace srrealmodule // End of file diff --git a/src/extensions/srreal_pickling.hpp b/src/extensions/srreal_pickling.hpp index 7acf5449..3ce3bf88 100644 --- a/src/extensions/srreal_pickling.hpp +++ b/src/extensions/srreal_pickling.hpp @@ -19,11 +19,7 @@ #ifndef SRREAL_PICKLING_HPP_INCLUDED #define SRREAL_PICKLING_HPP_INCLUDED -#include -#include -#include -#include -#include +#include #include #include @@ -31,94 +27,135 @@ #include #include +namespace nb = nanobind; + namespace srrealmodule { inline -void ensure_tuple_length(boost::python::tuple state, const int statelen) +void ensure_tuple_length(nb::tuple state, const int statelen) { - using namespace boost::python; - if (len(state) == statelen) return; - object emsg = ("expected %i-item tuple in call to " - "__setstate__; got %s" % make_tuple(statelen, state)); - PyErr_SetObject(PyExc_ValueError, emsg.ptr()); - throw_error_already_set(); + if (state.size() == statelen) return; + PyErr_Format( + PyExc_ValueError, + "expected %d-item tuple in call to __setstate__; got %zd", + statelen, + state.size() + ); + nb::raise_python_error(); } template -::boost::python::object serialization_tobytes(const T& tobj) +nb::bytes serialization_tobytes(const T& tobj) { std::string s = diffpy::serialization_tostring(tobj); - std::string::const_pointer pfirst = s.empty() ? NULL : &(s[0]); - boost::python::object rv( - boost::python::handle<>( - PyBytes_FromStringAndSize(pfirst, s.size())) - ); - return rv; + return nb::bytes(s.data(), s.size()); +} + +template +std::string bytes_to_string(const H& h) +{ + nb::bytes py_content(h); + return std::string( + static_cast(py_content.data()), + py_content.size() + ); } template -bool is_wrapper(::boost::python::object& obj) +bool is_wrapper(nb::object& obj) { - T* p = ::boost::python::extract(obj); - typedef ::boost::python::wrapper W; - bool rv = (dynamic_cast(p) != NULL); - return rv; + T* p = nullptr; + return nb::try_cast(obj, p, false) && p; } template -::boost::python::object resolve_state_object(::boost::python::object value) +nb::object resolve_state_object(nb::object value) { // return None if value holds a pristine C++ instance of T - ::boost::python::object rv; - if (is_wrapper(value)) rv = value; - return rv; + return is_wrapper(value) ? value : nb::none(); } template -void assign_state_object(T target, ::boost::python::object value) +void assign_state_object(T target, nb::object value) { - if (!value.is_none()) target = value; + if (!value.is_none()) target = nb::cast(value); +} + + +template +void construct_for_unpickle(T* tobj) +{ + new (tobj) T(); +} + + +template +T& ensure_instance_ready(nb::handle obj) +{ + T* tobj = nb::inst_ptr(obj); + + if (!nb::inst_ready(obj)) + { + construct_for_unpickle(tobj); + nb::inst_mark_ready(obj); + } + + return *tobj; } enum {DICT_IGNORE=false, DICT_PICKLE=true}; template -class SerializationPickleSuite : public boost::python::pickle_suite +class SerializationPickleSuite { public: - static boost::python::tuple getstate(boost::python::object obj) + template + static void bind(C& cls) + { + cls + .def("__getstate__", getstate) + .def("__setstate__", setstate) + ; + } + + static nb::tuple getstate(nb::object obj) { - using namespace std; - const T& tobj = boost::python::extract(obj); - boost::python::object content = serialization_tobytes(tobj); - boost::python::tuple rv = pickledict ? - boost::python::make_tuple(content, obj.attr("__dict__")) : - boost::python::make_tuple(content); - return rv; + const T& tobj = nb::cast(obj); + nb::bytes content = serialization_tobytes(*tobj); + + if constexpr (pickledict) + { + return nb::make_tuple( + content, + nb::getattr(obj, "__dict__") + ); + } + else + { + return nb::make_tuple(content); + } } static void setstate( - boost::python::object obj, boost::python::tuple state) + nb::object obj, nb::tuple state) { - using namespace std; - using namespace boost::python; - T& tobj = extract(obj); - int statelen = pickledict ? 2 : 1; + constexpr int statelen = pickledict ? 2 : 1; ensure_tuple_length(state, statelen); // load the C++ object - string content = extract(state[0]); - diffpy::serialization_fromstring(tobj, content); + T& tobj = ensure_instance_ready(obj); + diffpy::serialization_fromstring(tobj, bytes_to_string(state[0])); // restore the object's __dict__ - if (pickledict) + if constexpr (pickledict) { - dict d = extract(obj.attr("__dict__")); + nb::object dict_obj = nb::getattr(obj, "__dict__"); + nb::dict d = nb::borrow(dict_obj); d.update(state[1]); } } @@ -138,36 +175,42 @@ class PairQuantityPickleSuite : public: - static boost::python::tuple getstate(boost::python::object obj) + template + static void bind(C& cls) + { + cls + .def("__getstate__", getstate) + .def("__setstate__", setstate) + ; + } + + static nb::tuple getstate(nb::object obj) { - using namespace boost::python; using namespace diffpy::srreal; // store the original structure object - object stru = obj.attr("getStructure")(); + nb::object stru = obj.attr("getStructure")(); // temporarily remove structure from the pair quantity - T& pq = extract(obj); + T& pq = ensure_instance_ready(obj); StructureAdapterPtr pstru = replacePairQuantityStructure(pq, StructureAdapterPtr()); - object state0 = Super::getstate(obj); + nb::object state0 = Super::getstate(obj); // restore the original structure replacePairQuantityStructure(pq, pstru); - tuple rv = make_tuple(state0, stru); - return rv; + return nb::make_tuple(state0, stru); } static void setstate( - boost::python::object obj, boost::python::tuple state) + nb::object obj, nb::tuple state) { - using namespace boost::python; using namespace diffpy::srreal; ensure_tuple_length(state, 2); // restore the state using boost serialization - tuple st0 = extract(state[0]); - Super::setstate(obj, st0); + nb::tuple state0(state[0]); + Super::setstate(obj, state0); // restore the structure object - StructureAdapterPtr pstru = extract(state[1]); - T& pq = extract(obj); + StructureAdapterPtr pstru = nb::cast(state[1]); + T& pq = ensure_instance_ready(obj); replacePairQuantityStructure(pq, pstru); } @@ -175,64 +218,44 @@ class PairQuantityPickleSuite : template -class StructureAdapterPickleSuite : public boost::python::pickle_suite +class StructureAdapterPickleSuite { public: - static boost::python::tuple getinitargs( - diffpy::srreal::StructureAdapterPtr adpt) + template + static void bind(C& cls) { - using namespace boost; - python::tuple rv; - // if adapter has been created from Python, we can use the default - // Python constructor, i.e., __init__ with no arguments. - if (frompython(adpt)) return rv; - // otherwise the instance is from a non-wrapped C++ adapter, - // and we need to reconstruct it using boost::serialization - python::object content = serialization_tobytes(adpt); - rv = python::make_tuple(content); - return rv; + cls + .def("__getstate__", getstate) + .def("__setstate__", setstate) + ; } - static boost::python::tuple getstate(boost::python::object obj) + static nb::tuple getstate(nb::object obj) { - using namespace boost; - using namespace std; - using diffpy::srreal::StructureAdapterPtr; - StructureAdapterPtr adpt = - python::extract(obj); - python::object content; - // Store serialization data for a Python-built object - if (frompython(adpt)) - { - const T& tobj = boost::python::extract(obj); - content = serialization_tobytes(tobj); - } - python::tuple rv = - python::make_tuple(content, obj.attr("__dict__")); - return rv; + const T& tobj = nb::cast(obj); + nb::bytes content = serialization_tobytes(tobj); + return nb::make_tuple(content, nb::getattr(obj, "__dict__")); } static void setstate( - boost::python::object obj, boost::python::tuple state) + nb::object obj, nb::tuple state) { - using namespace std; - using namespace boost::python; ensure_tuple_length(state, 2); // Restore the C++ data from state[0] for Python built-objects. // state[0] is None for C++ objects and there is no need to do // anything as those were already restored by string constructor. - object st0 = state[0]; + nb::object st0 = nb::borrow(state[0]); if (!st0.is_none()) { - T& tobj = extract(obj); - string content = extract(st0); - diffpy::serialization_fromstring(tobj, content); + T& tobj = ensure_instance_ready(obj); + diffpy::serialization_fromstring(tobj, bytes_to_string(st0)); } // restore the object's __dict__ - dict d = extract(obj.attr("__dict__")); + nb::object dict_obj = nb::getattr(obj, "__dict__"); + nb::dict d = nb::borrow(dict_obj); d.update(state[1]); } @@ -252,7 +275,34 @@ class StructureAdapterPickleSuite : public boost::python::pickle_suite /// Helper function for creating Python constructor from string /// that restores c++ non-wrapped classes. -boost::python::object StructureAdapter_constructor(); +using diffpy::srreal::StructureAdapterPtr; + +StructureAdapterPtr +createStructureAdapterFromString(const std::string &content); + +template +std::shared_ptr +createAdapterFromString(const std::string &content) +{ + StructureAdapterPtr base = createStructureAdapterFromString(content); + + auto rv = std::dynamic_pointer_cast(base); + if (!rv) + { + throw nb::type_error( + "serialized content does not contain the requested adapter type" + ); + } + + return rv; +} + +template +void StructureAdapter_constructor(Adapter* self, const std::string& content) +{ + std::shared_ptr adpt = createAdapterFromString(content); + new (self) Adapter(*adpt); +} } // namespace srrealmodule diff --git a/src/extensions/wrap_AtomicStructureAdapter.cpp b/src/extensions/wrap_AtomicStructureAdapter.cpp index e6b3953a..dd78cf9a 100644 --- a/src/extensions/wrap_AtomicStructureAdapter.cpp +++ b/src/extensions/wrap_AtomicStructureAdapter.cpp @@ -19,8 +19,9 @@ * *****************************************************************************/ -#include -#include +#include +#include +#include #include #include @@ -36,12 +37,10 @@ namespace srrealmodule { // declarations -void sync_StructureDifference(boost::python::object obj); +void sync_StructureDifference(nb::object obj); namespace nswrap_AtomicStructureAdapter { -using namespace boost; -using namespace boost::python; using namespace diffpy::srreal; // docstrings ---------------------------------------------------------------- @@ -251,23 +250,23 @@ implicitly called from createBondGenerator.\n\ // Wrapper helpers for the class Atom -object get_xyz_cartn(Atom& a) +nb::object get_xyz_cartn(Atom& a) { return viewAsNumPyArray(a.xyz_cartn); } -void set_xyz_cartn(Atom& a, object value) +void set_xyz_cartn(Atom& a, nb::object value) { assignR3Vector(a.xyz_cartn, value); } -object get_uij_cartn(Atom& a) +nb::object get_uij_cartn(Atom& a) { return viewAsNumPyArray(a.uij_cartn); } -void set_uij_cartn(Atom& a, object& value) +void set_uij_cartn(Atom& a, nb::object& value) { assignR3Matrix(a.uij_cartn, value); } @@ -280,7 +279,7 @@ double get_xyz(const Atom& a) } template -void set_xyz(Atom& a, object value) +void set_xyz(Atom& a, nb::object value) { a.xyz_cartn[i] = extractdouble(value); } @@ -291,7 +290,7 @@ double get_occ(const Atom& a) return a.occupancy; } -void set_occ(Atom& a, object value) +void set_occ(Atom& a, nb::object value) { a.occupancy = extractdouble(value); } @@ -302,7 +301,7 @@ bool get_anisotropy(const Atom& a) return a.anisotropy; } -void set_anisotropy(Atom& a, object value) +void set_anisotropy(Atom& a, nb::object value) { a.anisotropy = bool(value); } @@ -316,7 +315,7 @@ double get_uc(const Atom& a) } template -void set_uc(Atom& a, object value) +void set_uc(Atom& a, nb::object value) { assert(i <= j); a.uij_cartn(i, j) = extractdouble(value); @@ -326,15 +325,15 @@ void set_uc(Atom& a, object value) // template wrapper class for overloading of clone and _customPQConfig template -class MakeWrapper : public T, public wrapper_srreal +class MakeWrapper : public T { public: - StructureAdapterPtr clone() const + NB_TRAMPOLINE(T, 3); + + StructureAdapterPtr clone() const override { - override f = this->get_override("clone"); - if (f) return f(); - else return this->default_clone(); + NB_OVERRIDE(clone); } StructureAdapterPtr default_clone() const @@ -343,11 +342,9 @@ class MakeWrapper : public T, public wrapper_srreal } - void customPQConfig(PairQuantity* pq) const + void customPQConfig(PairQuantity* pq) const override { - override f = this->get_override("_customPQConfig"); - if (f) f(ptr(pq)); - else this->default_customPQConfig(pq); + NB_OVERRIDE_NAME("_customPQConfig", customPQConfig, pq); } void default_customPQConfig(PairQuantity* pq) const @@ -356,15 +353,16 @@ class MakeWrapper : public T, public wrapper_srreal } - StructureDifference diff(StructureAdapterConstPtr other) const + StructureDifference diff(StructureAdapterConstPtr other) const override { - override f = this->get_override("diff"); - if (f) + nb::gil_scoped_acquire gil; + nb::detail::ticket ticket(nb_trampoline, "diff", false); + if (ticket.key.is_valid()) { - python::object sdobj = f(other); + nb::object sdobj = nb_trampoline.base().attr(ticket.key)(other); sync_StructureDifference(sdobj); StructureDifference& sd = - python::extract(sdobj); + nb::cast(sdobj); return sd; } return this->default_diff(other); @@ -391,34 +389,95 @@ class MakeWrapper : public T, public wrapper_srreal typedef MakeWrapper AtomicStructureAdapterWrap; typedef std::shared_ptr AtomicStructureAdapterWrapPtr; +typedef AtomicStructureAdapter::value_type data_type; + +class AtomAdapterIterator +{ + public: + + explicit AtomAdapterIterator(AtomicStructureAdapter& container) : + mcontainer(&container), midx(0) + { } + + Atom& next() + { + if (midx >= mcontainer->size()) + { + throw nb::stop_iteration(); + } + return (*mcontainer)[static_cast(midx++)]; + } + + private: + + AtomicStructureAdapter* mcontainer; + size_t midx; + +}; -class atomadapter_indexing : public vector_indexing_suite< - AtomicStructureAdapter, false, atomadapter_indexing> +class atomadapter_indexing : public nb::def_visitor { public: typedef AtomicStructureAdapter Container; - static object - get_slice(Container& container, index_type from, index_type to) + + template + void execute(Class& cls, const Extra&...) { + cls + .def("__len__", [](const Container& container) { + return container.size(); + }) + .def("__getitem__", + [](Container& container, int idx) -> Atom& { + return container[normalize_index(container, idx)]; + }, + nb::rv_policy::reference_internal) + .def("__getitem__", get_slice) + .def("__iter__", + [](Container& container) { + return AtomAdapterIterator(container); + }, + nb::keep_alive<0,1>()) + .def("__setitem__", + [](Container& container, int idx, const Atom& atom) { + container[normalize_index(container, idx)] = atom; + }) + .def("__delitem__", + [](Container& container, int idx) { + container.erase(normalize_index(container, idx)); + }) + ; + } + + + static nb::object + get_slice(Container& container, nb::slice slice) + { + const size_t n = container.countSites(); + auto [from, to, step, slice_len] = slice.compute(n); // make sure slice is of a correct type and has a copy // of any additional structure data. StructureAdapterPtr rv = container.clone(); - AtomicStructureAdapterPtr rva; - rva = std::static_pointer_cast(rv); + AtomicStructureAdapterPtr rva = + std::static_pointer_cast(rv); + rva->clear(); // handle index ranges for a valid and empty slice - if (from <= to) + Py_ssize_t idx = from; + for (size_t i = 0; i < slice_len; ++i, idx += step) { - rva->erase(rva->begin() + to, rva->end()); - rva->erase(rva->begin(), rva->begin() + from); + rva->append(container[static_cast(idx)]); } - else rva->clear(); + // save memory by making a new copy for short slices - const index_type halflength = rva->countSites() / 2; - const bool longslice = ((to - from) > halflength); - object pyrv(longslice ? rv : rv->clone()); - return pyrv; + const bool longslice = slice_len > n / 2; + AtomicStructureAdapterPtr out = + longslice + ? rva + : std::static_pointer_cast(rva->clone()); + + return nb::cast(out); } @@ -428,6 +487,14 @@ class atomadapter_indexing : public vector_indexing_suite< container.append(v); } + private: + + + static int normalize_index(const Container& container, int idx) + { + ensure_index_bounds(idx, -int(container.size()), container.size()); + return (idx >= 0) ? idx : int(container.size()) + idx; + } }; @@ -460,10 +527,10 @@ void atomadapter_reserve(AtomicStructureAdapter& adpt, int sz) typedef MakeWrapper PeriodicStructureAdapterWrap; typedef std::shared_ptr PeriodicStructureAdapterWrapPtr; -python::tuple periodicadapter_getlatpar(const PeriodicStructureAdapter& adpt) +nb::tuple periodicadapter_getlatpar(const PeriodicStructureAdapter& adpt) { const Lattice& L = adpt.getLattice(); - python::tuple rv = python::make_tuple( + nb::tuple rv = nb::make_tuple( L.a(), L.b(), L.c(), L.alpha(), L.beta(), L.gamma()); return rv; } @@ -481,7 +548,7 @@ crystaladapter_getsymmetryprecision(const CrystalStructureAdapter& adpt) void crystaladapter_addsymop(CrystalStructureAdapter& adpt, - python::object R, python::object t) + nb::object R, nb::object t) { static SymOpRotTrans op; assignR3Matrix(op.R, R); @@ -490,20 +557,19 @@ void crystaladapter_addsymop(CrystalStructureAdapter& adpt, } -python::tuple +nb::tuple crystaladapter_getsymop(const CrystalStructureAdapter& adpt, int idx) { ensure_index_bounds(idx, 0, adpt.countSymOps()); const SymOpRotTrans& op = adpt.getSymOp(idx); - python::tuple rv = python::make_tuple( + return nb::make_tuple( convertToNumPyArray(op.R), convertToNumPyArray(op.t)); - return rv; } DECLARE_PYLIST_METHOD_WRAPPER1(getEquivalentAtoms, getEquivalentAtoms_aslist) -python::object crystaladapter_getequivalentatoms( +nb::object crystaladapter_getequivalentatoms( const CrystalStructureAdapter& adpt, int idx) { ensure_index_bounds(idx, 0, adpt.countSymOps()); @@ -523,150 +589,171 @@ extern const char* doc_StructureAdapter_diff; // Wrapper definitions ------------------------------------------------------- -void wrap_AtomicStructureAdapter() +void wrap_AtomicStructureAdapter(nb::module_& m) { - namespace bp = boost::python; using namespace nswrap_AtomicStructureAdapter; using diffpy::srreal::hash_value; // class Atom - class_ atom_class("Atom", doc_Atom); + nb::class_ atom_class(m, "Atom", doc_Atom); // first define copy constructor and property helper methods atom_class - .def(init(bp::arg("atom"), doc_Atom_init_copy)) - .def(self == self) - .def(self != self) - .def(self < self) - .def(self > self) - .def(self <= self) - .def(self >= self) - .def("__hash__", hash_value) + .def(nb::init<>()) + .def(nb::init(), nb::arg("atom"), doc_Atom_init_copy) + .def(nb::self == nb::self) + .def(nb::self != nb::self) + .def(nb::self < nb::self) + .def(nb::self > nb::self) + .def(nb::self <= nb::self) + .def(nb::self >= nb::self) + .def("__hash__", static_cast(&hash_value)) .def("_get_xyz_cartn", get_xyz_cartn, - with_custodian_and_ward_postcall<0,1>()) + nb::keep_alive<0,1>()) .def("_get_uij_cartn", get_uij_cartn, - with_custodian_and_ward_postcall<0,1>()) + nb::keep_alive<0,1>()) ; // now we can finalize the Atom class interface atom_class - .def_readwrite("atomtype", &Atom::atomtype) - .add_property("xyz_cartn", - atom_class.attr("_get_xyz_cartn"), - set_xyz_cartn) - .add_property("xc", get_xyz<0>, set_xyz<0>, doc_Atom_xic) - .add_property("yc", get_xyz<1>, set_xyz<1>, doc_Atom_xic) - .add_property("zc", get_xyz<2>, set_xyz<2>, doc_Atom_xic) - .add_property("occupancy", get_occ, set_occ, doc_Atom_occ) - .add_property("anisotropy", get_anisotropy, set_anisotropy, + .def_rw("atomtype", &Atom::atomtype) + .def_prop_rw("xyz_cartn", + get_xyz_cartn, + set_xyz_cartn, + nb::keep_alive<0,1>()) + .def_prop_rw("xc", get_xyz<0>, set_xyz<0>, doc_Atom_xic) + .def_prop_rw("yc", get_xyz<1>, set_xyz<1>, doc_Atom_xic) + .def_prop_rw("zc", get_xyz<2>, set_xyz<2>, doc_Atom_xic) + .def_prop_rw("occupancy", get_occ, set_occ, doc_Atom_occ) + .def_prop_rw("anisotropy", get_anisotropy, set_anisotropy, doc_Atom_anisotropy) - .add_property("uij_cartn", - atom_class.attr("_get_uij_cartn"), - set_uij_cartn) - .add_property("uc11", get_uc<0, 0>, set_uc<0, 0>, doc_Atom_uijc) - .add_property("uc22", get_uc<1, 1>, set_uc<1, 1>, doc_Atom_uijc) - .add_property("uc33", get_uc<2, 2>, set_uc<2, 2>, doc_Atom_uijc) - .add_property("uc12", get_uc<0, 1>, set_uc<0, 1>, doc_Atom_uijc) - .add_property("uc13", get_uc<0, 2>, set_uc<0, 2>, doc_Atom_uijc) - .add_property("uc23", get_uc<1, 2>, set_uc<1, 2>, doc_Atom_uijc) - .def_pickle(SerializationPickleSuite()) + .def_prop_rw("uij_cartn", + get_uij_cartn, + set_uij_cartn, + nb::keep_alive<0,1>()) + .def_prop_rw("uc11", get_uc<0, 0>, set_uc<0, 0>, doc_Atom_uijc) + .def_prop_rw("uc22", get_uc<1, 1>, set_uc<1, 1>, doc_Atom_uijc) + .def_prop_rw("uc33", get_uc<2, 2>, set_uc<2, 2>, doc_Atom_uijc) + .def_prop_rw("uc12", get_uc<0, 1>, set_uc<0, 1>, doc_Atom_uijc) + .def_prop_rw("uc13", get_uc<0, 2>, set_uc<0, 2>, doc_Atom_uijc) + .def_prop_rw("uc23", get_uc<1, 2>, set_uc<1, 2>, doc_Atom_uijc) + ; + SerializationPickleSuite::bind(atom_class); + + nb::class_(m, "_AtomicStructureAdapterIterator") + .def("__iter__", [](AtomAdapterIterator& it) -> AtomAdapterIterator& { + return it; + }, nb::rv_policy::reference_internal) + .def("__next__", &AtomAdapterIterator::next, + nb::rv_policy::reference_internal) ; // class AtomicStructureAdapter - class_, - noncopyable, AtomicStructureAdapterWrapPtr>( - "AtomicStructureAdapter", doc_AtomicStructureAdapter) - .def("__init__", StructureAdapter_constructor(), + nb::class_ + adapter_class(m, "AtomicStructureAdapter", doc_AtomicStructureAdapter, + nb::dynamic_attr()); + adapter_class + .def(nb::init<>()) + .def("__init__", + StructureAdapter_constructor, + nb::arg("content"), doc_StructureAdapter___init__fromstring) .def(atomadapter_indexing()) - .def(self == self) - .def(self != self) + .def(nb::self == nb::self) + .def(nb::self != nb::self) .def("clone", &AtomicStructureAdapter::clone, - &AtomicStructureAdapterWrap::default_clone, doc_AtomicStructureAdapter_clone) .def("_customPQConfig", &AtomicStructureAdapter::customPQConfig, - &AtomicStructureAdapterWrap::default_customPQConfig, - python::arg("pqobj"), + nb::arg("pqobj"), doc_StructureAdapter__customPQConfig) .def("diff", &AtomicStructureAdapter::diff, - &AtomicStructureAdapterWrap::default_diff, - python::arg("other"), + nb::arg("other"), doc_StructureAdapter_diff) .def("insert", atomadapter_insert, - (bp::arg("index"), bp::arg("atom")), + nb::arg("index"), nb::arg("atom"), doc_AtomicStructureAdapter_insert) .def("append", &AtomicStructureAdapter::append, + nb::arg("atom"), doc_AtomicStructureAdapter_append) .def("pop", atomadapter_pop, - bp::arg("index"), doc_AtomicStructureAdapter_pop) + nb::arg("index"), doc_AtomicStructureAdapter_pop) .def("clear", &AtomicStructureAdapter::clear, doc_AtomicStructureAdapter_clear) .def("reserve", atomadapter_reserve, - bp::arg("sz"), doc_AtomicStructureAdapter_reserve) - .def_pickle(StructureAdapterPickleSuite()) + nb::arg("sz"), doc_AtomicStructureAdapter_reserve) ; + StructureAdapterPickleSuite::bind(adapter_class); // class PeriodicStructureAdapter - class_, - noncopyable, PeriodicStructureAdapterWrapPtr>( - "PeriodicStructureAdapter", doc_PeriodicStructureAdapter) - .def("__init__", StructureAdapter_constructor(), + nb::class_ + periodic_class(m, "PeriodicStructureAdapter", doc_PeriodicStructureAdapter, + nb::dynamic_attr()); + periodic_class + .def(nb::init<>()) + .def("__init__", + StructureAdapter_constructor, + nb::arg("content"), doc_StructureAdapter___init__fromstring) - .def(self == self) - .def(self != self) + .def(nb::self == nb::self) + .def(nb::self != nb::self) .def("clone", &PeriodicStructureAdapter::clone, - &PeriodicStructureAdapterWrap::default_clone, doc_PeriodicStructureAdapter_clone) .def("_customPQConfig", &PeriodicStructureAdapter::customPQConfig, - &PeriodicStructureAdapterWrap::default_customPQConfig, - python::arg("pqobj"), + nb::arg("pqobj"), doc_StructureAdapter__customPQConfig) .def("diff", &PeriodicStructureAdapter::diff, - &PeriodicStructureAdapterWrap::default_diff, - python::arg("other"), + nb::arg("other"), doc_StructureAdapter_diff) .def("getLatPar", periodicadapter_getlatpar, doc_PeriodicStructureAdapter_getLatPar) .def("setLatPar", &PeriodicStructureAdapter::setLatPar, - (bp::arg("a"), bp::arg("b"), bp::arg("c"), - bp::arg("alphadeg"), bp::arg("betadeg"), bp::arg("gammadeg")), + nb::arg("a"), nb::arg("b"), nb::arg("c"), + nb::arg("alphadeg"), nb::arg("betadeg"), + nb::arg("gammadeg"), doc_PeriodicStructureAdapter_setLatPar) .def("toCartesian", &PeriodicStructureAdapter::toCartesian, - bp::arg("atom"), doc_PeriodicStructureAdapter_toCartesian) + nb::arg("atom"), doc_PeriodicStructureAdapter_toCartesian) .def("toFractional", &PeriodicStructureAdapter::toFractional, - bp::arg("atom"), doc_PeriodicStructureAdapter_toFractional) - .def_pickle(StructureAdapterPickleSuite()) + nb::arg("atom"), doc_PeriodicStructureAdapter_toFractional) ; + StructureAdapterPickleSuite::bind(periodic_class); // class CrystalStructureAdapter - class_, - noncopyable, CrystalStructureAdapterWrapPtr>( - "CrystalStructureAdapter", doc_CrystalStructureAdapter) - .def("__init__", StructureAdapter_constructor(), + nb::class_ + crystal_class(m, "CrystalStructureAdapter", doc_CrystalStructureAdapter, + nb::dynamic_attr()); + crystal_class + .def(nb::init<>()) + .def("__init__", + StructureAdapter_constructor, + nb::arg("content"), doc_StructureAdapter___init__fromstring) - .def(self == self) - .def(self != self) + .def(nb::self == nb::self) + .def(nb::self != nb::self) .def("clone", &CrystalStructureAdapter::clone, - &CrystalStructureAdapterWrap::default_clone, doc_CrystalStructureAdapter_clone) .def("_customPQConfig", &CrystalStructureAdapter::customPQConfig, - &CrystalStructureAdapterWrap::default_customPQConfig, - python::arg("pqobj"), + nb::arg("pqobj"), doc_StructureAdapter__customPQConfig) .def("diff", &CrystalStructureAdapter::diff, - &CrystalStructureAdapterWrap::default_diff, - python::arg("other"), + nb::arg("other"), doc_StructureAdapter_diff) - .add_property("symmetryprecision", + .def_prop_rw("symmetryprecision", crystaladapter_getsymmetryprecision, &CrystalStructureAdapter::setSymmetryPrecision, doc_CrystalStructureAdapter_symmetryprecision) @@ -675,22 +762,22 @@ void wrap_AtomicStructureAdapter() .def("clearSymOps", &CrystalStructureAdapter::clearSymOps, doc_CrystalStructureAdapter_clearSymOps) .def("addSymOp", crystaladapter_addsymop, - (bp::arg("R"), bp::arg("t")), + nb::arg("R"), nb::arg("t"), doc_CrystalStructureAdapter_addSymOp) - .def("getSymOp", crystaladapter_getsymop, bp::arg("index"), + .def("getSymOp", crystaladapter_getsymop, nb::arg("index"), doc_CrystalStructureAdapter_getSymOp) .def("getEquivalentAtoms", - crystaladapter_getequivalentatoms, bp::arg("index"), + crystaladapter_getequivalentatoms, nb::arg("index"), doc_CrystalStructureAdapter_getEquivalentAtoms) .def("expandLatticeAtom", expandLatticeAtom_aslist, - bp::arg("atom"), + nb::arg("atom"), doc_CrystalStructureAdapter_expandLatticeAtom) .def("updateSymmetryPositions", &CrystalStructureAdapter::updateSymmetryPositions, doc_CrystalStructureAdapter_updateSymmetryPositions) - .def_pickle(StructureAdapterPickleSuite()) ; + StructureAdapterPickleSuite::bind(crystal_class); } From c53cca4207c495f11a22fe1a85cde2d897815dab Mon Sep 17 00:00:00 2001 From: symscae Date: Wed, 24 Jun 2026 22:14:07 -0700 Subject: [PATCH 07/18] migrated bindings for AtomRadiiTable, Attributes, BaseBondGenerator and BondCalculator --- src/extensions/wrap_AtomRadiiTable.cpp | 89 ++++++++++++++--------- src/extensions/wrap_Attributes.cpp | 56 +++++++------- src/extensions/wrap_BaseBondGenerator.cpp | 30 ++++---- src/extensions/wrap_BondCalculator.cpp | 43 ++++++----- 4 files changed, 120 insertions(+), 98 deletions(-) diff --git a/src/extensions/wrap_AtomRadiiTable.cpp b/src/extensions/wrap_AtomRadiiTable.cpp index ed91a8ac..80dd1c8f 100644 --- a/src/extensions/wrap_AtomRadiiTable.cpp +++ b/src/extensions/wrap_AtomRadiiTable.cpp @@ -16,9 +16,9 @@ * *****************************************************************************/ -#include -#include -#include +#include +#include +#include #include "srreal_converters.hpp" #include "srreal_pickling.hpp" @@ -27,10 +27,11 @@ #include #include +namespace nb = nanobind; + namespace srrealmodule { namespace nswrap_AtomRadiiTable { -using namespace boost::python; using namespace diffpy::srreal; // docstrings ---------------------------------------------------------------- @@ -150,43 +151,64 @@ DECLARE_PYDICT_METHOD_WRAPPER(getAllCustom, getAllCustom_asdict) // Helper class for overloads of AtomRadiiTable methods from Python class AtomRadiiTableWrap : - public AtomRadiiTable, - public wrapper_srreal + public AtomRadiiTable { public: + NB_TRAMPOLINE(AtomRadiiTable, 4); + // HasClassRegistry methods - AtomRadiiTablePtr create() const + AtomRadiiTablePtr create() const override { - object rv = this->get_pure_virtual_override("create")(); + nb::gil_scoped_acquire gil; + nb::detail::ticket ticket(nb_trampoline, "create", true); + + if (!ticket.key.is_valid()) + { + throw nb::type_error( + "pure virtual method AtomRadiiTable.create() called" + ); + } + + nb::object rv = nb_trampoline.base().attr(ticket.key)(); return mconfigurator.fetch(rv); } - AtomRadiiTablePtr clone() const + AtomRadiiTablePtr clone() const override { - return this->get_pure_virtual_override("clone")(); + NB_OVERRIDE_PURE(clone); } - const std::string& type() const + const std::string& type() const override { - object tp = this->get_pure_virtual_override("type")(); - mtype = extract(tp); + nb::gil_scoped_acquire gil; + nb::detail::ticket ticket(nb_trampoline, "type", true); + + if (!ticket.key.is_valid()) + { + throw nb::type_error( + "pure virtual method AtomRadiiTable.type() called" + ); + } + + nb::object tp = nb_trampoline.base().attr(ticket.key)(); + mtype = nb::cast(tp); return mtype; } // own methods - double standardLookup(const std::string& smbl) const + double standardLookup(const std::string& smbl) const override { - return this->get_pure_virtual_override("_standardLookup")(smbl); + NB_OVERRIDE_PURE_NAME("_standardLookup", standardLookup, smbl); } protected: // HasClassRegistry method - void setupRegisteredObject(AtomRadiiTablePtr p) const + void setupRegisteredObject(AtomRadiiTablePtr p) const override { mconfigurator.setup(p); } @@ -211,29 +233,31 @@ class AtomRadiiTableWrap : // Wrapper definition -------------------------------------------------------- -void wrap_AtomRadiiTable() +void wrap_AtomRadiiTable(nb::module_& m) { using namespace nswrap_AtomRadiiTable; - using boost::noncopyable; - class_ - atomradiitable("AtomRadiiTable", doc_AtomRadiiTable); + nb::class_ + atomradiitable(m, "AtomRadiiTable", doc_AtomRadiiTable, + nb::dynamic_attr()); wrap_registry_methods(atomradiitable) + .def(nb::init<>()) .def("lookup", - &AtomRadiiTable::lookup, arg("smbl"), + &AtomRadiiTable::lookup, nb::arg("smbl"), doc_AtomRadiiTable_lookup) .def("_standardLookup", &AtomRadiiTable::standardLookup, - arg("smbl"), doc_AtomRadiiTable__standardLookup) + nb::arg("smbl"), doc_AtomRadiiTable__standardLookup) .def("setCustom", &AtomRadiiTable::setCustom, - (arg("smbl"), arg("radius")), + nb::arg("smbl"), nb::arg("radius"), doc_AtomRadiiTable_setCustom) .def("fromString", &AtomRadiiTable::fromString, + nb::arg("s"), doc_AtomRadiiTable_fromString) .def("resetCustom", - &AtomRadiiTable::resetCustom, arg("smbl"), + &AtomRadiiTable::resetCustom, nb::arg("smbl"), doc_AtomRadiiTable_resetCustom) .def("resetAll", &AtomRadiiTable::resetAll, @@ -242,33 +266,32 @@ void wrap_AtomRadiiTable() getAllCustom_asdict, doc_AtomRadiiTable_getAllCustom) .def("toString", - &AtomRadiiTable::toString, arg("separator")=",", + &AtomRadiiTable::toString, nb::arg("separator")=",", doc_AtomRadiiTable_toString) - .def_pickle(SerializationPickleSuite()) ; + SerializationPickleSuite::bind(atomradiitable); - register_ptr_to_python(); - - class_ >( - "ConstantRadiiTable", doc_ConstantRadiiTable) + nb::class_ + constantradiitable(m, "ConstantRadiiTable", doc_ConstantRadiiTable); // docstring updates + constantradiitable .def("create", &ConstantRadiiTable::create, doc_ConstantRadiiTable_create) .def("clone", &ConstantRadiiTable::clone, doc_ConstantRadiiTable_clone) .def("_standardLookup", &ConstantRadiiTable::standardLookup, - arg("smbl"), doc_ConstantRadiiTable__standardLookup) + nb::arg("smbl"), doc_ConstantRadiiTable__standardLookup) // own methods .def("setDefault", &ConstantRadiiTable::setDefault, - arg("radius"), + nb::arg("radius"), doc_ConstantRadiiTable_setDefault) .def("getDefault", &ConstantRadiiTable::getDefault, doc_ConstantRadiiTable_getDefault) - .def_pickle(SerializationPickleSuite()) ; + SerializationPickleSuite::bind(constantradiitable); } diff --git a/src/extensions/wrap_Attributes.cpp b/src/extensions/wrap_Attributes.cpp index b574f48c..dd68bcc5 100644 --- a/src/extensions/wrap_Attributes.cpp +++ b/src/extensions/wrap_Attributes.cpp @@ -16,7 +16,8 @@ * *****************************************************************************/ -#include +#include +#include #include @@ -24,11 +25,11 @@ #include "srreal_converters.hpp" +namespace nb = nanobind; + namespace srrealmodule { namespace nswrap_Attributes { -using std::string; -using namespace boost; using namespace diffpy::attributes; // docstrings ---------------------------------------------------------------- @@ -103,8 +104,8 @@ class PythonDoubleAttribute : public BaseDoubleAttribute public: // constructor - PythonDoubleAttribute(python::object owner, - python::object getter, python::object setter) + PythonDoubleAttribute(nb::object owner, + nb::object getter, nb::object setter) { // PythonDoubleAttribute needs to know its Python owner, but it // has to use a borrowed reference otherwise the owner would be @@ -116,28 +117,29 @@ class PythonDoubleAttribute : public BaseDoubleAttribute } - double getValue(const Attributes* obj) const + double getValue(const Attributes* obj) const override { + nb::gil_scoped_acquire gil; // verify that mowner is indeed the obj wrapper - python::object owner(python::borrowed(mowner)); - assert(obj == python::extract(owner)); - python::object pyrv = mgetter(owner); - double rv = python::extract(pyrv); - return rv; + nb::object owner = nb::borrow(mowner); + assert(obj == nb::cast(owner)); + nb::object pyrv = mgetter(owner); + return nb::cast(pyrv); } - void setValue(Attributes* obj, double value) + void setValue(Attributes* obj, double value) override { + nb::gil_scoped_acquire gil; if (this->isreadonly()) throwDoubleAttributeReadOnly(); // verify that mowner is indeed the obj wrapper - python::object owner(python::borrowed(mowner)); - assert(obj == python::extract(owner)); + nb::object owner = nb::borrow(mowner); + assert(obj == nb::cast(owner)); msetter(owner, value); } - bool isreadonly() const + bool isreadonly() const override { return (msetter.is_none()); } @@ -146,24 +148,24 @@ class PythonDoubleAttribute : public BaseDoubleAttribute // data PyObject* mowner; - python::object mgetter; - python::object msetter; + nb::object mgetter; + nb::object msetter; }; // class PythonDoubleAttribute -void registerPythonDoubleAttribute(python::object owner, - const string& name, python::object g, python::object s) +void registerPythonDoubleAttribute(nb::object owner, + const std::string& name, nb::object g, nb::object s) { // when neither getter no setter are specified, // make it use normal python attribute access if (g.is_none() && s.is_none()) { - python::object mod = python::import("diffpy.srreal.attributes"); + nb::object mod = nb::module_::import_("diffpy.srreal.attributes"); g = mod.attr("_pyattrgetter")(name); s = mod.attr("_pyattrsetter")(name); } - Attributes* cowner = python::extract(owner); + Attributes* cowner = nb::cast(owner); BaseDoubleAttribute* pa = new PythonDoubleAttribute(owner, g, s); registerBaseDoubleAttribute(cowner, name, pa); } @@ -172,13 +174,13 @@ void registerPythonDoubleAttribute(python::object owner, // Wrapper definition -------------------------------------------------------- -void wrap_Attributes() +void wrap_Attributes(nb::module_& m) { using namespace nswrap_Attributes; - using namespace boost::python; - const python::object None; + const nb::object None; // ready for class definition - class_("Attributes", doc_Attributes) + nb::class_(m, "Attributes", nb::dynamic_attr(), doc_Attributes) + .def(nb::init<>()) .def("_getDoubleAttr", &Attributes::getDoubleAttr, doc_Attributes__getDoubleAttr) .def("_setDoubleAttr", &Attributes::setDoubleAttr, @@ -193,7 +195,9 @@ void wrap_Attributes() doc_Attributes__namesOfWritableDoubleAttributes) .def("_registerDoubleAttribute", registerPythonDoubleAttribute, - (python::arg("getter")=None, python::arg("setter")=None), + nb::arg("name"), + nb::arg("getter") = nb::none(), + nb::arg("setter") = nb::none(), doc_Attributes__registerDoubleAttribute) ; } diff --git a/src/extensions/wrap_BaseBondGenerator.cpp b/src/extensions/wrap_BaseBondGenerator.cpp index 87c2295c..8a5f6b96 100644 --- a/src/extensions/wrap_BaseBondGenerator.cpp +++ b/src/extensions/wrap_BaseBondGenerator.cpp @@ -18,10 +18,7 @@ * *****************************************************************************/ -#include -#include -#include -#include +#include #include @@ -30,6 +27,8 @@ #include "srreal_converters.hpp" +namespace nb = nanobind; + namespace srrealmodule { namespace nswrap_BaseBondGenerator { @@ -152,14 +151,13 @@ DECLARE_PYARRAY_METHOD_WRAPPER(Ucartesian1, Ucartesian1_asarray) // Wrapper definition -------------------------------------------------------- -void wrap_BaseBondGenerator() +void wrap_BaseBondGenerator(nb::module_& m) { - using namespace boost::python; using namespace diffpy::srreal; using namespace nswrap_BaseBondGenerator; - class_("BaseBondGenerator", doc_BaseBondGenerator, - init()) + nb::class_(m, "BaseBondGenerator", doc_BaseBondGenerator) + .def(nb::init()) .def("rewind", &BaseBondGenerator::rewind, doc_BaseBondGenerator_rewind) .def("finished", &BaseBondGenerator::finished, @@ -167,19 +165,19 @@ void wrap_BaseBondGenerator() .def("next", &BaseBondGenerator::next, doc_BaseBondGenerator_next) .def("selectAnchorSite", &BaseBondGenerator::selectAnchorSite, - arg("anchor"), doc_BaseBondGenerator_selectAnchorSite) + nb::arg("anchor"), doc_BaseBondGenerator_selectAnchorSite) .def("selectSiteRange", &BaseBondGenerator::selectSiteRange, - (arg("first"), arg("last")), + nb::arg("first"), nb::arg("last"), doc_BaseBondGenerator_selectSiteRange) .def("setRmin", &BaseBondGenerator::setRmin, - arg("rmin"), doc_BaseBondGenerator_setRmin) + nb::arg("rmin"), doc_BaseBondGenerator_setRmin) .def("setRmax", &BaseBondGenerator::setRmax, - arg("rmax"), doc_BaseBondGenerator_setRmax) + nb::arg("rmax"), doc_BaseBondGenerator_setRmax) .def("getRmin", &BaseBondGenerator::getRmin, - return_value_policy(), + nb::rv_policy::copy, doc_BaseBondGenerator_getRmin) .def("getRmax", &BaseBondGenerator::getRmax, - return_value_policy(), + nb::rv_policy::copy, doc_BaseBondGenerator_getRmax) .def("site0", &BaseBondGenerator::site0, doc_BaseBondGenerator_site0) @@ -192,7 +190,7 @@ void wrap_BaseBondGenerator() .def("r1", r1_asarray, doc_BaseBondGenerator_r1) .def("distance", &BaseBondGenerator::distance, - return_value_policy(), + nb::rv_policy::copy, doc_BaseBondGenerator_distance) .def("r01", r01_asarray, doc_BaseBondGenerator_r01) @@ -203,8 +201,6 @@ void wrap_BaseBondGenerator() .def("msd", &BaseBondGenerator::msd, doc_BaseBondGenerator_msd) ; - - register_ptr_to_python(); } } // namespace srrealmodule diff --git a/src/extensions/wrap_BondCalculator.cpp b/src/extensions/wrap_BondCalculator.cpp index 7b317188..44f90443 100644 --- a/src/extensions/wrap_BondCalculator.cpp +++ b/src/extensions/wrap_BondCalculator.cpp @@ -16,18 +16,18 @@ * *****************************************************************************/ -#include +#include #include #include "srreal_converters.hpp" #include "srreal_pickling.hpp" +namespace nb = nanobind; + namespace srrealmodule { namespace nswrap_BondCalculator { -namespace bp = boost::python; -using namespace boost::python; using namespace diffpy::srreal; // docstrings ---------------------------------------------------------------- @@ -82,18 +82,16 @@ exclusive cone filter in a new direction.\n\ void filter_cone(BondCalculator& obj, - object cartesiandir, double degrees) + nb::object cartesiandir, double degrees) { if (len(cartesiandir) != 3) { - const char* emsg = "cartesiandir must be a 3-element array."; - PyErr_SetString(PyExc_ValueError, emsg); - bp::throw_error_already_set(); + throw nb::value_error("cartesiandir must be a 3-element array."); } R3::Vector cdir; - cdir[0] = extract(cartesiandir[0]); - cdir[1] = extract(cartesiandir[1]); - cdir[2] = extract(cartesiandir[2]); + cdir[0] = nb::cast(cartesiandir[0]); + cdir[1] = nb::cast(cartesiandir[1]); + cdir[2] = nb::cast(cartesiandir[2]); obj.filterCone(cdir, degrees); } @@ -101,38 +99,39 @@ void filter_cone(BondCalculator& obj, // Wrapper definition -------------------------------------------------------- -void wrap_BondCalculator() +void wrap_BondCalculator(nb::module_& m) { using namespace nswrap_BondCalculator; - class_ - >("BondCalculator", doc_BondCalculator) - .add_property("distances", + nb::class_ + bondcalculator(m, "BondCalculator", doc_BondCalculator); + bondcalculator + .def(nb::init<>()) + .def_prop_ro("distances", distances_asarray, doc_BondCalculator_distances) - .add_property("directions", + .def_prop_ro("directions", directions_asarray, doc_BondCalculator_directions) - .add_property("sites0", + .def_prop_ro("sites0", sites0_asarray, doc_BondCalculator_sites0) - .add_property("sites1", + .def_prop_ro("sites1", sites1_asarray, doc_BondCalculator_sites1) - .add_property("types0", + .def_prop_ro("types0", types0_aschararray, doc_BondCalculator_types0) - .add_property("types1", + .def_prop_ro("types1", types1_aschararray, doc_BondCalculator_types1) .def("filterCone", filter_cone, - (bp::arg("cartesiandir"), bp::arg("degrees")), + nb::arg("cartesiandir"), nb::arg("degrees"), doc_BondCalculator_filterCone) .def("filterOff", &BondCalculator::filterOff, doc_BondCalculator_filterOff) - .def_pickle(PairQuantityPickleSuite()) ; - + PairQuantityPickleSuite::bind(bondcalculator); } } // namespace srrealmodule From b5d4d4768db3c7eb6c5a6f027913aaa84aca9bb3 Mon Sep 17 00:00:00 2001 From: symscae Date: Wed, 24 Jun 2026 22:47:59 -0700 Subject: [PATCH 08/18] migrated bindings for BVParametersTable, BVSCalculator, EventTicker and libdiffpy_version --- src/extensions/wrap_BVParametersTable.cpp | 213 ++++++++++++++-------- src/extensions/wrap_BVSCalculator.cpp | 26 +-- src/extensions/wrap_EventTicker.cpp | 41 +++-- src/extensions/wrap_libdiffpy_version.cpp | 16 +- 4 files changed, 178 insertions(+), 118 deletions(-) diff --git a/src/extensions/wrap_BVParametersTable.cpp b/src/extensions/wrap_BVParametersTable.cpp index 9022c55e..b35dba90 100644 --- a/src/extensions/wrap_BVParametersTable.cpp +++ b/src/extensions/wrap_BVParametersTable.cpp @@ -16,10 +16,8 @@ * *****************************************************************************/ -#include -#include -#include -#include +#include +#include #include @@ -31,10 +29,11 @@ #define BVPARMCIF "bvparm2011.cif" +namespace nb = nanobind; + namespace srrealmodule { namespace nswrap_BVParametersTable { -using namespace boost::python; using namespace diffpy::srreal; // docstrings ---------------------------------------------------------------- @@ -218,27 +217,43 @@ Return a set of BVParam objects.\n\ // wrappers ------------------------------------------------------------------ -BOOST_PYTHON_MEMBER_FUNCTION_OVERLOADS(setcustom6, setCustom, 6, 7) DECLARE_PYSET_METHOD_WRAPPER(getAll, getAll_asset) +void setCustom6(BVParametersTable& obj, + const std::string& atom0, + int valence0, + const std::string& atom1, + int valence1, + double Ro, + double B, + const std::string& ref_id) { + obj.setCustom(atom0, valence0, atom1, valence1, Ro, B, ref_id); +} -object repr_BVParam(const BVParam& bp) +nb::object repr_BVParam(const BVParam& bp) { - if (bp == BVParametersTable::none()) return object("BVParam()"); - object rv = ("BVParam(%r, %i, %r, %i, Ro=%s, B=%s, ref_id=%r)" % - make_tuple(bp.matom0, bp.mvalence0, bp.matom1, bp.mvalence1, - bp.mRo, bp.mB, bp.mref_id)); - return rv; + if (bp == BVParametersTable::none()) return nb::str("BVParam()"); + return nb::str( + "BVParam({!r}, {:d}, {!r}, {:d}, Ro={}, B={}, ref_id={!r})" + ).attr("format")( + bp.matom0, + bp.mvalence0, + bp.matom1, + bp.mvalence1, + bp.mRo, + bp.mB, + bp.mref_id + ); } -object singleton_none() +nb::object singleton_none() { const char* nameofnone = "__BVParam_singleton_none"; - object mod = import("diffpy.srreal.srreal_ext"); + nb::module_ mod = nb::module_::import_("diffpy.srreal.srreal_ext"); static bool noneassigned = false; if (!noneassigned) { - mod.attr(nameofnone) = object(BVParametersTable::none()); + mod.attr(nameofnone) = nb::cast(BVParametersTable::none(), nb::rv_policy::copy); noneassigned = true; } return mod.attr(nameofnone); @@ -248,89 +263,131 @@ object singleton_none() // Wrapper definition -------------------------------------------------------- -void wrap_BVParametersTable() +void wrap_BVParametersTable(nb::module_& m) { using namespace nswrap_BVParametersTable; - using std::string; - - class_("BVParam", doc_BVParam) - .def(init(doc_BVParam___init__, - (arg("atom0"), arg("valence0"), - arg("atom1"), arg("valence1"), arg("Ro")=0.0, arg("B")=0.0, - arg("ref_id")=""))) + + nb::class_ bvparam(m, "BVParam", doc_BVParam); + bvparam + .def(nb::init(), + nb::arg("atom0"), + nb::arg("valence0"), + nb::arg("atom1"), + nb::arg("valence1"), + nb::arg("Ro") = 0.0, + nb::arg("B") = 0.0, + nb::arg("ref_id") = "", + doc_BVParam___init__) .def("__repr__", repr_BVParam, doc_BVParam___repr__) - .def(self == self) - .def(self != self) + .def(nb::self == nb::self) + .def(nb::self != nb::self) .def("bondvalence", &BVParam::bondvalence, - arg("distance"), doc_BVParam_bondvalence) + nb::arg("distance"), doc_BVParam_bondvalence) .def("bondvalenceToDistance", &BVParam::bondvalenceToDistance, - arg("valence"), doc_BVParam_bondvalenceToDistance) + nb::arg("valence"), doc_BVParam_bondvalenceToDistance) .def("setFromCifLine", &BVParam::setFromCifLine, doc_BVParam_setFromCifLine) - .def_readonly("atom0", &BVParam::matom0, doc_BVParam_atom0) - .def_readonly("valence0", &BVParam::mvalence0, doc_BVParam_valence0) - .def_readonly("atom1", &BVParam::matom1, doc_BVParam_atom1) - .def_readonly("valence1", &BVParam::mvalence1, doc_BVParam_valence1) - .def_readwrite("Ro", &BVParam::mRo, doc_BVParam_Ro) - .def_readwrite("B", &BVParam::mB, doc_BVParam_B) - .def_readwrite("ref_id", &BVParam::mref_id, doc_BVParam_ref_id) - .def_pickle(SerializationPickleSuite()) + .def_ro("atom0", &BVParam::matom0, doc_BVParam_atom0) + .def_ro("valence0", &BVParam::mvalence0, doc_BVParam_valence0) + .def_ro("atom1", &BVParam::matom1, doc_BVParam_atom1) + .def_ro("valence1", &BVParam::mvalence1, doc_BVParam_valence1) + .def_rw("Ro", &BVParam::mRo, doc_BVParam_Ro) + .def_rw("B", &BVParam::mB, doc_BVParam_B) + .def_rw("ref_id", &BVParam::mref_id, doc_BVParam_ref_id) ; + SerializationPickleSuite::bind(bvparam); - typedef const BVParam&(BVParametersTable::*bptb_bvparam_1)( - const BVParam&) const; - typedef const BVParam&(BVParametersTable::*bptb_bvparam_2)( - const string&, const string&) const; - typedef const BVParam&(BVParametersTable::*bptb_bvparam_4)( - const string&, int, const string&, int) const; - typedef void(BVParametersTable::*bptb_void_1)( - const BVParam&); - typedef void(BVParametersTable::*bptb_void_4)( - const string&, int, const string&, int); - - class_("BVParametersTable", doc_BVParametersTable) - .def("none", singleton_none, doc_BVParametersTable_none) - .staticmethod("none") + nb::class_ + bvtable(m, "BVParametersTable", doc_BVParametersTable); + bvtable + .def(nb::init<>()) + .def_static("none", singleton_none, doc_BVParametersTable_none) .def("getAtomValence", &BVParametersTable::getAtomValence, - arg("smbl"), + nb::arg("smbl"), doc_BVParametersTable_getAtomValence) .def("setAtomValence", &BVParametersTable::setAtomValence, - (arg("smbl"), arg("value")), + nb::arg("smbl"), nb::arg("value"), doc_BVParametersTable_setAtomValence) .def("resetAtomValences", &BVParametersTable::resetAtomValences, doc_BVParametersTable_resetAtomValences) - .def("lookup", bptb_bvparam_1(&BVParametersTable::lookup), - arg("bvparam"), doc_BVParametersTable_lookup1, - return_value_policy()) - .def("lookup", bptb_bvparam_2(&BVParametersTable::lookup), - (arg("smbl0"), arg("smbl1")), - doc_BVParametersTable_lookup2, - return_value_policy()) - .def("lookup", bptb_bvparam_4(&BVParametersTable::lookup), - (arg("atom0"), arg("valence0"), arg("atom1"), arg("valence1")), - doc_BVParametersTable_lookup4, - return_value_policy()) - .def("setCustom", bptb_void_1(&BVParametersTable::setCustom), - arg("bvparm"), doc_BVParametersTable_setCustom1) - .def("setCustom", (void(BVParametersTable::*)(const string&, int, - const string&, int, double, double, string)) NULL, - setcustom6((arg("atom0"), arg("valence0"), arg("atom1"), arg("valence1"), - arg("Ro"), arg("B"), arg("ref_id")=""), - doc_BVParametersTable_setCustom6)) - .def("resetCustom", bptb_void_1(&BVParametersTable::resetCustom), - doc_BVParametersTable_resetCustom1) - .def("resetCustom", bptb_void_4(&BVParametersTable::resetCustom), - (arg("atom0"), arg("valence0"), arg("atom1"), arg("valence1")), - doc_BVParametersTable_resetCustom4) + .def("lookup", + [](const BVParametersTable &obj, + const BVParam &bvparam) -> BVParam { + return obj.lookup(bvparam); + }, + nb::arg("bvparam"), + doc_BVParametersTable_lookup1) + + .def("lookup", + [](const BVParametersTable &obj, + const std::string &smbl0, + const std::string &smbl1) -> BVParam { + return obj.lookup(smbl0, smbl1); + }, + nb::arg("smbl0"), + nb::arg("smbl1"), + doc_BVParametersTable_lookup2) + + .def("lookup", + [](const BVParametersTable &obj, + const std::string &atom0, + int valence0, + const std::string &atom1, + int valence1) -> BVParam { + return obj.lookup(atom0, valence0, atom1, valence1); + }, + nb::arg("atom0"), + nb::arg("valence0"), + nb::arg("atom1"), + nb::arg("valence1"), + doc_BVParametersTable_lookup4) + + .def("setCustom", + [](BVParametersTable &obj, const BVParam &bvparam) { + obj.setCustom(bvparam); + }, + nb::arg("bvparam"), + doc_BVParametersTable_setCustom1) + + .def("setCustom", + &setCustom6, + nb::arg("atom0"), + nb::arg("valence0"), + nb::arg("atom1"), + nb::arg("valence1"), + nb::arg("Ro"), + nb::arg("B"), + nb::arg("ref_id") = "", + doc_BVParametersTable_setCustom6) + .def("resetCustom", + [](BVParametersTable &obj, const BVParam &bvparam) { + obj.resetCustom(bvparam); + }, + nb::arg("bvparam"), + doc_BVParametersTable_resetCustom1) + + .def("resetCustom", + [](BVParametersTable &obj, + const std::string &atom0, + int valence0, + const std::string &atom1, + int valence1) { + obj.resetCustom(atom0, valence0, atom1, valence1); + }, + nb::arg("atom0"), + nb::arg("valence0"), + nb::arg("atom1"), + nb::arg("valence1"), + doc_BVParametersTable_resetCustom4) .def("resetAll", &BVParametersTable::resetAll, doc_BVParametersTable_resetAll) .def("getAll", getAll_asset, doc_BVParametersTable_getAll) - .def_pickle(SerializationPickleSuite()) ; - - register_ptr_to_python(); + SerializationPickleSuite::bind(bvtable); + } } // namespace srrealmodule diff --git a/src/extensions/wrap_BVSCalculator.cpp b/src/extensions/wrap_BVSCalculator.cpp index 1096b0cb..c39b6328 100644 --- a/src/extensions/wrap_BVSCalculator.cpp +++ b/src/extensions/wrap_BVSCalculator.cpp @@ -16,17 +16,18 @@ * *****************************************************************************/ -#include +#include #include #include "srreal_converters.hpp" #include "srreal_pickling.hpp" +namespace nb = nanobind; + namespace srrealmodule { namespace nswrap_BVSCalculator { -using namespace boost::python; using namespace diffpy::srreal; // docstrings ---------------------------------------------------------------- @@ -81,26 +82,27 @@ void setbvparamtable(BVSCalculator& obj, BVParametersTablePtr bptb) // Wrapper definition -------------------------------------------------------- -void wrap_BVSCalculator() +void wrap_BVSCalculator(nb::module_& m) { using namespace nswrap_BVSCalculator; - class_ >("BVSCalculator", - doc_BVSCalculator) - .add_property("value", value_asarray, + nb::class_ bvscalculator(m, "BVSCalculator", + doc_BVSCalculator); + bvscalculator + .def_prop_ro("value", value_asarray, doc_BVSCalculator_value) - .add_property("valences", valences_asarray, + .def_prop_ro("valences", valences_asarray, doc_BVSCalculator_valences) - .add_property("bvdiff", bvdiff_asarray, + .def_prop_ro("bvdiff", bvdiff_asarray, doc_BVSCalculator_bvdiff) - .add_property("bvmsdiff", &BVSCalculator::bvmsdiff, + .def_prop_ro("bvmsdiff", &BVSCalculator::bvmsdiff, doc_BVSCalculator_bvmsdiff) - .add_property("bvrmsdiff", &BVSCalculator::bvrmsdiff, + .def_prop_ro("bvrmsdiff", &BVSCalculator::bvrmsdiff, doc_BVSCalculator_bvrmsdiff) - .add_property("bvparamtable", getbvparamtable, setbvparamtable, + .def_prop_rw("bvparamtable", getbvparamtable, setbvparamtable, doc_BVSCalculator_bvparamtable) - .def_pickle(PairQuantityPickleSuite()) ; + PairQuantityPickleSuite::bind(bvscalculator); } diff --git a/src/extensions/wrap_EventTicker.cpp b/src/extensions/wrap_EventTicker.cpp index b26bedfb..84d697dd 100644 --- a/src/extensions/wrap_EventTicker.cpp +++ b/src/extensions/wrap_EventTicker.cpp @@ -16,12 +16,15 @@ * *****************************************************************************/ -#include +#include +#include #include #include "srreal_pickling.hpp" +namespace nb = nanobind; + namespace srrealmodule { namespace nswrap_EventTicker { @@ -66,47 +69,47 @@ and the first one is zero unless there was an integer overflow.\n\ // getter for the _value property -python::tuple gettickervalue(const EventTicker& tc) +nb::tuple gettickervalue(const EventTicker& tc) { EventTicker::value_type v = tc.value(); - return python::make_tuple(v.first, v.second); + return nb::make_tuple(v.first, v.second); } // representation of EventTicker objects -python::object repr_EventTicker(const EventTicker& tc) +nb::object repr_EventTicker(const EventTicker& tc) { - python::object rv = ("EventTicker(%i, %i)" % gettickervalue(tc)); - return rv; + auto v = tc.value(); + return nb::str("EventTicker({}, {})").attr("format")(v.first, v.second); } } // namespace nswrap_EventTicker // Wrapper definition -------------------------------------------------------- -void wrap_EventTicker() +void wrap_EventTicker(nb::module_& m) { using namespace nswrap_EventTicker; - using namespace boost::python; - class_("EventTicker", doc_EventTicker) - .def(init(doc_EventTicker_cp)) + nb::class_ eventticker(m, "EventTicker", doc_EventTicker); + eventticker + .def(nb::init(), doc_EventTicker_cp) .def("__repr__", repr_EventTicker, doc_EventTicker___repr__) - .def(self < self) - .def(self <= self) - .def(self > self) - .def(self >= self) - .def(self == self) - .def(self != self) + .def(nb::self < nb::self) + .def(nb::self <= nb::self) + .def(nb::self > nb::self) + .def(nb::self >= nb::self) + .def(nb::self == nb::self) + .def(nb::self != nb::self) .def("click", &EventTicker::click, doc_EventTicker_click) .def("updateFrom", &EventTicker::updateFrom, - python::arg("other"), + nb::arg("other"), doc_EventTicker_updateFrom) - .add_property("_value", gettickervalue, + .def_prop_ro("_value", gettickervalue, doc_EventTicker__value) - .def_pickle(SerializationPickleSuite()) ; + SerializationPickleSuite::bind(eventticker); } diff --git a/src/extensions/wrap_libdiffpy_version.cpp b/src/extensions/wrap_libdiffpy_version.cpp index 80347f3d..b640841d 100644 --- a/src/extensions/wrap_libdiffpy_version.cpp +++ b/src/extensions/wrap_libdiffpy_version.cpp @@ -17,14 +17,13 @@ *****************************************************************************/ #include -#include -#include +#include + +namespace nb = nanobind; namespace srrealmodule { namespace nswrap_libdiffpy_version { -using namespace boost; - // docstrings ---------------------------------------------------------------- const char* doc__get_libdiffpy_version_info_dict = "\ @@ -33,9 +32,9 @@ Return dictionary with version data for the loaded libdiffpy library.\n\ // wrappers ------------------------------------------------------------------ -python::dict get_libdiffpy_version_info_dict() +nb::dict get_libdiffpy_version_info_dict() { - python::dict rv; + nb::dict rv; // Obtain version data from runtime values. rv["version"] = libdiffpy_version_info::version; rv["version_str"] = libdiffpy_version_info::version_str; @@ -52,12 +51,11 @@ python::dict get_libdiffpy_version_info_dict() // Wrapper definition -------------------------------------------------------- -void wrap_libdiffpy_version() +void wrap_libdiffpy_version(nb::module_& m) { using namespace nswrap_libdiffpy_version; - using boost::python::def; - def("_get_libdiffpy_version_info_dict", + m.def("_get_libdiffpy_version_info_dict", get_libdiffpy_version_info_dict, doc__get_libdiffpy_version_info_dict); From 8f349d6719ad5ac35677c2c0a0a35cae32f92b0f Mon Sep 17 00:00:00 2001 From: symscae Date: Thu, 25 Jun 2026 00:10:48 -0700 Subject: [PATCH 09/18] migrated bindings for ObjCrystAdapters, OverlapCalculator, PairQuantity and PDFBaseline --- src/extensions/wrap_ObjCrystAdapters.cpp | 16 +- src/extensions/wrap_OverlapCalculator.cpp | 82 +++--- src/extensions/wrap_PDFBaseline.cpp | 81 ++++-- src/extensions/wrap_PairQuantity.cpp | 330 +++++++++------------- 4 files changed, 238 insertions(+), 271 deletions(-) diff --git a/src/extensions/wrap_ObjCrystAdapters.cpp b/src/extensions/wrap_ObjCrystAdapters.cpp index 02553850..250d12a8 100644 --- a/src/extensions/wrap_ObjCrystAdapters.cpp +++ b/src/extensions/wrap_ObjCrystAdapters.cpp @@ -17,7 +17,7 @@ * *****************************************************************************/ -#include +#include #include @@ -28,6 +28,8 @@ #include #endif +namespace nb = nanobind; + namespace srrealmodule { namespace nswrap_ObjCrystAdapters { @@ -81,10 +83,7 @@ StructureAdapterPtr convertObjCrystCrystal(const Crystal& mol) StructureAdapterPtr convertObjCrystMolecule(python::object mol) { - std::string emsg = "ObjCryst support not available."; - PyErr_SetString(PyExc_TypeError, emsg.c_str()); - boost::python::throw_error_already_set(); - abort(); + throw nb::type_error("ObjCryst support not available."); } StructureAdapterPtr convertObjCrystCrystal(python::object cryst) @@ -99,14 +98,13 @@ StructureAdapterPtr convertObjCrystCrystal(python::object cryst) // Wrapper definitions ------------------------------------------------------- -void wrap_ObjCrystAdapters() +void wrap_ObjCrystAdapters(nb::module_& m) { using namespace nswrap_ObjCrystAdapters; - using namespace boost::python; - def("convertObjCrystMolecule", + m.def("convertObjCrystMolecule", convertObjCrystMolecule, doc_convertObjCrystMolecule); - def("convertObjCrystCrystal", + m.def("convertObjCrystCrystal", convertObjCrystCrystal, doc_convertObjCrystCrystal); } diff --git a/src/extensions/wrap_OverlapCalculator.cpp b/src/extensions/wrap_OverlapCalculator.cpp index 4fef2dff..765cf162 100644 --- a/src/extensions/wrap_OverlapCalculator.cpp +++ b/src/extensions/wrap_OverlapCalculator.cpp @@ -16,17 +16,18 @@ * *****************************************************************************/ -#include +#include #include #include "srreal_converters.hpp" #include "srreal_pickling.hpp" +namespace nb = nanobind; + namespace srrealmodule { namespace nswrap_OverlapCalculator { -using namespace boost::python; using namespace diffpy::srreal; // docstrings ---------------------------------------------------------------- @@ -156,7 +157,7 @@ AtomRadiiTablePtr getatomradiitable(OverlapCalculator& obj) DECLARE_BYTYPE_SETTER_WRAPPER(setAtomRadiiTable, setatomradiitable) -double flip_diff_total(const OverlapCalculator& obj, object i, object j) +double flip_diff_total(const OverlapCalculator& obj, nb::object i, nb::object j) { int i1 = extractint(i); int j1 = extractint(j); @@ -164,7 +165,7 @@ double flip_diff_total(const OverlapCalculator& obj, object i, object j) } -double flip_diff_mean(const OverlapCalculator& obj, object i, object j) +double flip_diff_mean(const OverlapCalculator& obj, nb::object i, nb::object j) { int i1 = extractint(i); int j1 = extractint(j); @@ -172,10 +173,10 @@ double flip_diff_mean(const OverlapCalculator& obj, object i, object j) } -object get_neighbor_sites(const OverlapCalculator& obj, object i) +nb::object get_neighbor_sites(const OverlapCalculator& obj, nb::object i) { int i1 = extractint(i); - object rv = convertToPythonSet(obj.getNeighborSites(i1)); + nb::object rv = convertToPythonSet(obj.getNeighborSites(i1)); return rv; } @@ -189,11 +190,20 @@ class OverlapCalculatorPickleSuite : public: - static boost::python::tuple getstate(boost::python::object obj) + template + static void bind(C& cls) + { + cls + .def("__getstate__", getstate) + .def("__setstate__", setstate) + ; + } + + + static nb::tuple getstate(nb::object obj) { - namespace bp = boost::python; - bp::object tb = obj.attr("atomradiitable"); - bp::tuple rv = bp::make_tuple( + nb::object tb = obj.attr("atomradiitable"); + nb::tuple rv = nb::make_tuple( Super::getstate(obj), resolve_state_object(tb) ); @@ -202,13 +212,12 @@ class OverlapCalculatorPickleSuite : static void setstate( - boost::python::object obj, boost::python::tuple state) + nb::object obj, nb::tuple state) { - namespace bp = boost::python; ensure_tuple_length(state, 2); // restore the state using boost serialization - bp::tuple st0 = bp::extract(state[0]); - Super::setstate(obj, st0); + nb::tuple state0(state[0]); + Super::setstate(obj, state0); // atomradiitable is present only when restoring Python class assign_state_object(obj.attr("atomradiitable"), state[1]); } @@ -218,73 +227,74 @@ class OverlapCalculatorPickleSuite : // Wrapper definition -------------------------------------------------------- -void wrap_OverlapCalculator() +void wrap_OverlapCalculator(nb::module_& m) { using namespace nswrap_OverlapCalculator; - class_ >("OverlapCalculator", - doc_OverlapCalculator) - .add_property("overlaps", + nb::class_ overlapcalculator(m, "OverlapCalculator", + doc_OverlapCalculator); + overlapcalculator + .def_prop_ro("overlaps", overlaps_asarray, doc_OverlapCalculator_overlaps) - .add_property("distances", + .def_prop_ro("distances", distances_asarray, doc_OverlapCalculator_distances) - .add_property("directions", + .def_prop_ro("directions", directions_asarray, doc_OverlapCalculator_directions) - .add_property("sites0", + .def_prop_ro("sites0", sites0_asarray, doc_OverlapCalculator_sites0) - .add_property("sites1", + .def_prop_ro("sites1", sites1_asarray, doc_OverlapCalculator_sites1) - .add_property("types0", + .def_prop_ro("types0", types0_aschararray, doc_OverlapCalculator_types0) - .add_property("types1", + .def_prop_ro("types1", types1_aschararray, doc_OverlapCalculator_types1) - .add_property("sitesquareoverlaps", + .def_prop_ro("sitesquareoverlaps", siteSquareOverlaps_asarray, doc_OverlapCalculator_sitesquareoverlaps) - .add_property("totalsquareoverlap", + .def_prop_ro("totalsquareoverlap", &OverlapCalculator::totalSquareOverlap, doc_OverlapCalculator_totalsquareoverlap) - .add_property("meansquareoverlap", + .def_prop_ro("meansquareoverlap", &OverlapCalculator::meanSquareOverlap, doc_OverlapCalculator_meansquareoverlap) .def("flipDiffTotal", flip_diff_total, - (arg("i"), arg("j")), + nb::arg("i"), nb::arg("j"), doc_OverlapCalculator_flipDiffTotal) .def("flipDiffMean", flip_diff_mean, doc_OverlapCalculator_flipDiffMean) - .add_property("gradients", + .def_prop_ro("gradients", gradients_asarray, doc_OverlapCalculator_gradients) .def("getNeighborSites", get_neighbor_sites, - arg("i"), + nb::arg("i"), doc_OverlapCalculator_getNeighborSites) - .add_property("coordinations", + .def_prop_ro("coordinations", coordinations_asarray, doc_OverlapCalculator_coordinations) .def("coordinationByTypes", coordinationByTypes_asdict, - arg("i"), + nb::arg("i"), doc_OverlapCalculator_coordinationByTypes) - .add_property("neighborhoods", + .def_prop_ro("neighborhoods", neighborhoods_aslistset, doc_OverlapCalculator_neighborhoods) - .add_property("atomradiitable", + .def_prop_rw("atomradiitable", getatomradiitable, setatomradiitable, doc_OverlapCalculator_atomradiitable) - .def_pickle(OverlapCalculatorPickleSuite()) ; - + OverlapCalculatorPickleSuite::bind(overlapcalculator); + } } // namespace srrealmodule diff --git a/src/extensions/wrap_PDFBaseline.cpp b/src/extensions/wrap_PDFBaseline.cpp index 90120045..8458f824 100644 --- a/src/extensions/wrap_PDFBaseline.cpp +++ b/src/extensions/wrap_PDFBaseline.cpp @@ -17,10 +17,8 @@ * *****************************************************************************/ -#include -#include -#include -#include +#include +#include #include #include @@ -37,11 +35,11 @@ #include "srreal_pickling.hpp" #include "srreal_registry.hpp" +namespace nb = nanobind; + namespace srrealmodule { namespace nswrap_PDFBaseline { -using namespace boost; -using namespace boost::python; using namespace diffpy::srreal; // docstrings ---------------------------------------------------------------- @@ -72,28 +70,49 @@ PDF baseline function equal to (slope * r).\n\ // Helper class allows overload of the PDFBaseline methods from Python. class PDFBaselineWrap : - public PDFBaseline, - public wrapper_srreal + public PDFBaseline { public: + NB_TRAMPOLINE(PDFBaseline, 4); + // HasClassRegistry methods PDFBaselinePtr create() const { - object rv = this->get_pure_virtual_override("create")(); + nb::gil_scoped_acquire gil; + nb::detail::ticket ticket(nb_trampoline, "create", true); + + if (!ticket.key.is_valid()) + { + throw nb::type_error( + "pure virtual method PDFBaseline.create() called" + ); + } + + nb::object rv = nb_trampoline.base().attr(ticket.key)(); return mconfigurator.fetch(rv); } PDFBaselinePtr clone() const { - return this->get_pure_virtual_override("clone")(); + NB_OVERRIDE_PURE(clone); } const std::string& type() const { - python::object tp = this->get_pure_virtual_override("type")(); - mtype = python::extract(tp); + nb::gil_scoped_acquire gil; + nb::detail::ticket ticket(nb_trampoline, "type", true); + + if (!ticket.key.is_valid()) + { + throw nb::type_error( + "pure virtual method PDFBaseline.type() called" + ); + } + + nb::object tp = nb_trampoline.base().attr(ticket.key)(); + mtype = nb::cast(tp); return mtype; } @@ -101,7 +120,7 @@ class PDFBaselineWrap : double operator()(const double& x) const { - return this->get_pure_virtual_override("__call__")(x); + NB_OVERRIDE_PURE_NAME("__call__", operator(), x); } protected: @@ -130,7 +149,7 @@ class PDFBaselineWrap : }; // class PDFBaselineWrap -object callnparray(const PDFBaseline* obj, object& x) +nb::object callnparray(const PDFBaseline* obj, nb::object& x) { NumPyArray_DoublePtr xx = extractNumPyDoubleArray(x); NumPyArray_DoublePtr yy = createNumPyDoubleArrayLike(xx.first); @@ -145,30 +164,32 @@ object callnparray(const PDFBaseline* obj, object& x) // Wrapper definition -------------------------------------------------------- -void wrap_PDFBaseline() +void wrap_PDFBaseline(nb::module_& m) { using namespace nswrap_PDFBaseline; using diffpy::Attributes; - namespace bp = boost::python; - class_, noncopyable> - pdfbaseline("PDFBaseline", doc_PDFBaseline); + nb::class_ + pdfbaseline(m, "PDFBaseline", nb::dynamic_attr(), doc_PDFBaseline); wrap_registry_methods(pdfbaseline) + .def(nb::init<>()) .def("__call__", callnparray, - bp::arg("r_array")) + nb::arg("r_array")) .def("__call__", &PDFBaseline::operator(), - bp::arg("r"), doc_PDFBaseline___call__) - .def_pickle(SerializationPickleSuite()) + nb::arg("r"), doc_PDFBaseline___call__) ; - - register_ptr_to_python(); - - class_ >( - "ZeroBaseline", doc_ZeroBaseline) - .def_pickle(SerializationPickleSuite()); - class_ >( - "LinearBaseline", doc_ZeroBaseline) - .def_pickle(SerializationPickleSuite()); + SerializationPickleSuite::bind(pdfbaseline); + + nb::class_ + zerobaseline(m, "ZeroBaseline", doc_ZeroBaseline); + zerobaseline + .def(nb::init<>()); + SerializationPickleSuite::bind(zerobaseline); + nb::class_ + linearbaseline(m,"LinearBaseline", doc_LinearBaseline); + linearbaseline + .def(nb::init<>()); + SerializationPickleSuite::bind(linearbaseline); } diff --git a/src/extensions/wrap_PairQuantity.cpp b/src/extensions/wrap_PairQuantity.cpp index d3403ce5..f467feb4 100644 --- a/src/extensions/wrap_PairQuantity.cpp +++ b/src/extensions/wrap_PairQuantity.cpp @@ -29,11 +29,9 @@ * *****************************************************************************/ -#include -#include -#include -#include -#include +#include +#include +#include #include @@ -42,47 +40,14 @@ #include "srreal_validators.hpp" #include +#include -namespace boost { -namespace python { - -struct make_pybytes -{ - PyObject* operator()(const std::string& s) const - { - std::string::const_pointer pfirst = s.empty() ? NULL : &(s[0]); - PyObject* rv = PyBytes_FromStringAndSize(pfirst, s.size()); - return rv; - } - - - const PyTypeObject* get_pytype() const - { - return &PyBytes_Type; - } -}; - - -struct copy_string_to_pybytes -{ - template - struct apply - { - // Fail if this result conversion is used for function - // that does not return std::string. - BOOST_MPL_ASSERT(( is_same )); - typedef make_pybytes type; - }; -}; - -} // namespace python -} // namespace boost +namespace nb = nanobind; +NB_MAKE_OPAQUE(diffpy::srreal::QuantityType); namespace srrealmodule { namespace nswrap_PairQuantity { -using namespace boost; -using namespace boost::python; using namespace diffpy::srreal; // docstrings ---------------------------------------------------------------- @@ -361,22 +326,31 @@ Reference to the internal vector of total contributions.\n\ // wrappers ------------------------------------------------------------------ +inline nb::bytes string_to_pybytes(const std::string &s) +{ + return nb::bytes(s.data(), s.size()); +} + // representation of QuantityType objects -python::object repr_QuantityType(const QuantityType& v) +nb::object repr_QuantityType(const QuantityType& v) { - python::object rv = ("QuantityType%r" % - python::make_tuple(python::tuple(v))); - return rv; + nb::tuple t = nb::make_tuple(v.size()); + + for (size_t i = 0; i < v.size(); ++i) { + t[i] = nb::cast(v[i]); + } + + return nb::str("QuantityType{}").attr("format")(nb::repr(t)); } // PairQuantity::eval is a template non-constant method and // needs an explicit wrapper function. -python::object eval_asarray(PairQuantity& obj, python::object& a) +nb::object eval_asarray(PairQuantity& obj, nb::object& a) { QuantityType value = (a.is_none()) ? obj.eval() : obj.eval(a); - python::object rv = convertToNumPyArray(value); + nb::object rv = convertToNumPyArray(value); return rv; } @@ -400,10 +374,9 @@ std::string stringevaluatortype(PQEvaluatorType tp) case CHECK: return evtp_CHECK; } - const char* emsg = "Unknown internal value of PQEvaluatorType."; - PyErr_SetString(PyExc_NotImplementedError, emsg); - throw_error_already_set(); - abort(); + PyErr_SetString(PyExc_NotImplementedError, + "Unknown internal value of PQEvaluatorType."); + throw nb::python_error(); } @@ -418,11 +391,10 @@ void setevaluatortype(PairQuantity& pq, const std::string& tp) if (tp == evtp_BASIC) return pq.setEvaluatorType(BASIC); if (tp == evtp_OPTIMIZED) return pq.setEvaluatorType(OPTIMIZED); if (tp == evtp_CHECK) return pq.setEvaluatorType(CHECK); - python::object emsg = ("evaluatortype must be one of %r." % - python::make_tuple(python::make_tuple( - evtp_BASIC, evtp_OPTIMIZED, evtp_CHECK))); - PyErr_SetObject(PyExc_ValueError, emsg.ptr()); - throw_error_already_set(); + + throw nb::value_error( + "evaluatortype must be one of ('BASIC', 'OPTIMIZED', 'CHECK')." + ); } // support for the evaluatortypeused read-only property @@ -434,22 +406,19 @@ std::string getevaluatortypeused(const PairQuantity& obj) // support "all", "ALL" and integer iterables in setPairMask -std::vector parsepairindex(python::object i) +std::vector parsepairindex(nb::object i) { std::vector rv; // string equal "all" or "ALL" - python::extract gets(i); - if (gets.check()) + if (nb::isinstance(i)) { - python::str lc_all(PairQuantity::ALLATOMSSTR); - python::str uc_all = lc_all.upper(); - if (i != lc_all && i != uc_all) + std::string s = nb::cast(i); + + if (s != PairQuantity::ALLATOMSSTR && s != "ALL") { - python::object emsg = ("String argument must be %r or %r." % - python::make_tuple(lc_all, uc_all)); - PyErr_SetObject(PyExc_ValueError, emsg.ptr()); - throw_error_already_set(); + throw nb::value_error("String argument must be 'all' or 'ALL'."); } + rv.push_back(PairQuantity::ALLATOMSINT); return rv; } @@ -460,36 +429,29 @@ std::vector parsepairindex(python::object i) // support string iterables in setTypeMask -std::vector parsepairtypes( - python::extract& getsmbli, python::object smbli) +std::vector parsepairtypes(nb::object smbl) { - std::vector rv; - if (getsmbli.check()) - { - rv.push_back(getsmbli()); - } - else + if (nb::isinstance(smbl)) { - python::stl_input_iterator first(smbli), last; - rv.assign(first, last); + return { nb::cast(smbl) }; } - return rv; + + return nb::cast>(smbl); } -void mask_all_pairs(PairQuantity& obj, python::object msk) +void mask_all_pairs(PairQuantity& obj, nb::object msk) { - bool mask = msk; - obj.maskAllPairs(mask); + obj.maskAllPairs(nb::cast(msk)); } void set_pair_mask(PairQuantity& obj, - python::object i, python::object j, python::object msk, - python::object others) + nb::object i, nb::object j, nb::object msk, + nb::object others) { if (!others.is_none()) mask_all_pairs(obj, others); - bool mask = msk; + bool mask = nb::cast(msk); // short circuit for normal call with scalar values if (!isiterable(i) && !isiterable(j)) { @@ -512,28 +474,22 @@ void set_pair_mask(PairQuantity& obj, void set_type_mask(PairQuantity& obj, - python::object smbli, python::object smblj, python::object msk, - python::object others) + nb::object smbli, nb::object smblj, nb::object msk, + nb::object others) { using namespace std; if (!others.is_none()) mask_all_pairs(obj, others); - python::extract getsmbli(smbli); - python::extract getsmblj(smblj); - bool mask = msk; - // short circuit for normal call - if (getsmbli.check() && getsmblj.check()) - { - obj.setTypeMask(getsmbli(), getsmblj(), mask); - return; - } - vector isymbols = parsepairtypes(getsmbli, smbli); - vector jsymbols = parsepairtypes(getsmblj, smblj); + + bool mask = nb::cast(msk); + + std::vector isymbols = parsepairtypes(smbli); + std::vector jsymbols = parsepairtypes(smblj); vector::const_iterator tii, tjj; - for (tii = isymbols.begin(); tii != isymbols.end(); ++tii) + for (const std::string &ti : isymbols) { - for (tjj = jsymbols.begin(); tjj != jsymbols.end(); ++tjj) + for (const std::string &tj : jsymbols) { - obj.setTypeMask(*tii, *tjj, mask); + obj.setTypeMask(ti, tj, mask); } } } @@ -541,11 +497,10 @@ void set_type_mask(PairQuantity& obj, // provide a copy method for convenient deepcopy of the object -python::object pqcopy(python::object pqobj) +nb::object pqcopy(nb::object pqobj) { - python::object copy = python::import("copy").attr("copy"); - python::object rv = copy(pqobj); - return rv; + nb::object copy = nb::module_::import_("copy").attr("copy"); + return copy(pqobj); } // Helper C++ class for publicizing the protected methods. @@ -615,18 +570,17 @@ class PairQuantityExposed : public PairQuantity // methods from Python. class PairQuantityWrap : - public PairQuantityExposed, - public wrapper + public PairQuantityExposed { public: + NB_TRAMPOLINE(PairQuantityExposed, 10); + // Make getParallelData overridable from Python. - std::string getParallelData() const + std::string getParallelData() const override { - override f = this->get_override("_getParallelData"); - if (f) return f(); - return this->default_getParallelData(); + NB_OVERRIDE_NAME("_getParallelData", getParallelData); } std::string default_getParallelData() const @@ -636,16 +590,18 @@ class PairQuantityWrap : // Make the ticker method overridable from Python - diffpy::eventticker::EventTicker& ticker() const + diffpy::eventticker::EventTicker& ticker() const override { using diffpy::eventticker::EventTicker; - override f = this->get_override("ticker"); - if (f) + nb::gil_scoped_acquire gil; + nb::detail::ticket ticket(nb_trampoline, "ticker", false); + + if (ticket.key.is_valid()) { - // avoid "dangling reference error" when used from C++ - object ptic = f(); - return extract(ptic); + nb::object ptic = nb_trampoline.base().attr(ticket.key)(); + return nb::cast(ptic); } + return this->default_ticker(); } @@ -657,11 +613,9 @@ class PairQuantityWrap : // Make the protected virtual methods public so they // can be exported to Python and overridden as well. - void resizeValue(size_t sz) + void resizeValue(size_t sz) override { - override f = this->get_override("_resizeValue"); - if (f) f(sz); - else this->default_resizeValue(sz); + NB_OVERRIDE_NAME("_resizeValue", resizeValue, sz); } void default_resizeValue(size_t sz) @@ -670,11 +624,9 @@ class PairQuantityWrap : } - void resetValue() + void resetValue() override { - override f = this->get_override("_resetValue"); - if (f) f(); - else this->default_resetValue(); + NB_OVERRIDE_NAME("_resetValue", resetValue); } void default_resetValue() @@ -683,11 +635,9 @@ class PairQuantityWrap : } - void configureBondGenerator(BaseBondGenerator& bnds) const + void configureBondGenerator(BaseBondGenerator& bnds) const override { - override f = this->get_override("_configureBondGenerator"); - if (f) f(ptr(&bnds)); - else this->default_configureBondGenerator(bnds); + NB_OVERRIDE_NAME("_configureBondGenerator", configureBondGenerator, bnds); } void default_configureBondGenerator(BaseBondGenerator& bnds) const @@ -697,11 +647,12 @@ class PairQuantityWrap : void addPairContribution(const BaseBondGenerator& bnds, - int summationscale) + int summationscale) override { - override f = this->get_override("_addPairContribution"); - if (f) f(ptr(&bnds), summationscale); - else this->default_addPairContribution(bnds, summationscale); + NB_OVERRIDE_NAME("_addPairContribution", + addPairContribution, + bnds, + summationscale); } void default_addPairContribution(const BaseBondGenerator& bnds, @@ -711,11 +662,9 @@ class PairQuantityWrap : } - void executeParallelMerge(const std::string& pdata) + void executeParallelMerge(const std::string& pdata) override { - override f = this->get_override("_executeParallelMerge"); - if (f) f(pdata); - else this->default_executeParallelMerge(pdata); + NB_OVERRIDE_NAME("_executeParallelMerge", executeParallelMerge, pdata); } void default_executeParallelMerge(const std::string& pdata) @@ -724,11 +673,9 @@ class PairQuantityWrap : } - void finishValue() + void finishValue() override { - override f = this->get_override("_finishValue"); - if (f) f(); - else this->default_finishValue(); + NB_OVERRIDE_NAME("_finishValue", finishValue); } void default_finishValue() @@ -737,11 +684,9 @@ class PairQuantityWrap : } - void stashPartialValue() + void stashPartialValue() override { - override f = this->get_override("_stashPartialValue"); - if (f) f(); - else this->default_stashPartialValue(); + NB_OVERRIDE_NAME("_stashPartialValue", stashPartialValue); } void default_stashPartialValue() @@ -750,11 +695,9 @@ class PairQuantityWrap : } - void restorePartialValue() + void restorePartialValue() override { - override f = this->get_override("_restorePartialValue"); - if (f) f(); - else this->default_restorePartialValue(); + NB_OVERRIDE_NAME("_restorePartialValue", restorePartialValue); } void default_restorePartialValue() @@ -768,126 +711,121 @@ class PairQuantityWrap : // Wrapper definition -------------------------------------------------------- -void wrap_PairQuantity() +void wrap_PairQuantity(nb::module_& m) { using namespace nswrap_PairQuantity; using diffpy::Attributes; - const python::object None; + const nb::object None; typedef StructureAdapterPtr&(PairQuantity::*getstru)(); - class_("QuantityType") - .def(vector_indexing_suite()) - .def("__repr__", repr_QuantityType) - ; + nb::bind_vector(m, "QuantityType") + .def("__repr__", &repr_QuantityType); - class_ >("BasePairQuantity") - .def("eval", eval_asarray, python::arg("stru")=None, + nb::class_ + basepq(m, "BasePairQuantity"); + basepq + .def("eval", eval_asarray, nb::arg("stru")=None, doc_BasePairQuantity_eval) - .add_property("value", value_asarray, + .def_prop_ro("value", value_asarray, doc_BasePairQuantity_value) .def("_mergeParallelData", &PairQuantity::mergeParallelData, - (python::arg("pdata"), python::arg("ncpu")), + nb::arg("pdata"), nb::arg("ncpu"), doc_BasePairQuantity__mergeParallelData) .def("_getParallelData", &PairQuantity::getParallelData, - return_value_policy(), doc_BasePairQuantity__getParallelData) - .def("setStructure", &PairQuantity::setStructure, - python::arg("stru"), + .def("setStructure", [](PairQuantity &pq, nb::object stru) + { + pq.setStructure(stru); + }, + nb::arg("stru"), doc_BasePairQuantity_setStructure) .def("getStructure", getstru(&PairQuantity::getStructure), - return_value_policy(), doc_BasePairQuantity_getStructure) .def("_setupParallelRun", &PairQuantity::setupParallelRun, - (python::arg("cpuindex"), python::arg("ncpu")), + nb::arg("cpuindex"), nb::arg("ncpu"), doc_BasePairQuantity__setupParallelRun) - .add_property("evaluatortype", + .def_prop_rw("evaluatortype", getevaluatortype, setevaluatortype, doc_BasePairQuantity_evaluatortype) - .add_property("evaluatortypeused", + .def_prop_ro("evaluatortypeused", getevaluatortypeused, doc_BasePairQuantity_evaluatortypeused) .def("maskAllPairs", mask_all_pairs, - python::arg("mask"), + nb::arg("mask"), doc_BasePairQuantity_maskAllPairs) .def("invertMask", &PairQuantity::invertMask, doc_BasePairQuantity_invertMask) .def("setPairMask", set_pair_mask, - (python::arg("i"), python::arg("j"), python::arg("mask"), - python::arg("others")=None), + nb::arg("i"), nb::arg("j"), nb::arg("mask"), + nb::arg("others")=None, doc_BasePairQuantity_setPairMask) .def("getPairMask", &PairQuantity::getPairMask, - (python::arg("i"), python::arg("j")), + nb::arg("i"), nb::arg("j"), doc_BasePairQuantity_getPairMask) .def("setTypeMask", set_type_mask, - (python::arg("tpi"), python::arg("tpj"), python::arg("mask"), - python::arg("others")=None), + nb::arg("tpi"), nb::arg("tpj"), nb::arg("mask"), + nb::arg("others")=None, doc_BasePairQuantity_setTypeMask) .def("getTypeMask", &PairQuantity::getTypeMask, - (python::arg("tpi"), python::arg("tpj")), + nb::arg("tpi"), nb::arg("tpj"), doc_BasePairQuantity_getTypeMask) .def("ticker", &PairQuantity::ticker, - return_internal_reference<>(), + nb::rv_policy::reference_internal, doc_BasePairQuantity_ticker) .def("copy", pqcopy, doc_BasePairQuantity_copy) ; - class_, - noncopyable>("PairQuantity", doc_PairQuantity) + nb::class_ pq(m, "PairQuantity", doc_PairQuantity); + pq .def("ticker", &PairQuantityExposed::ticker, - &PairQuantityWrap::default_ticker, - return_internal_reference<>(), + nb::rv_policy::reference_internal, doc_PairQuantity_ticker) - .def("_getParallelData", - &PairQuantityExposed::getParallelData, - &PairQuantityWrap::default_getParallelData, - return_value_policy(), + .def("_getParallelData", [](const PairQuantityExposed &pq) + { + return string_to_pybytes(pq.getParallelData()); + }, doc_PairQuantity__getParallelData) .def("_resizeValue", &PairQuantityExposed::resizeValue, - &PairQuantityWrap::default_resizeValue, - python::arg("sz"), + nb::arg("sz"), doc_PairQuantity__resizeValue) .def("_resetValue", &PairQuantityExposed::resetValue, - &PairQuantityWrap::default_resetValue, doc_PairQuantity__resetValue) .def("_configureBondGenerator", &PairQuantityExposed::configureBondGenerator, - &PairQuantityWrap::default_configureBondGenerator, - python::arg("bnds"), + nb::arg("bnds"), doc_PairQuantity__configureBondGenerator) .def("_addPairContribution", &PairQuantityExposed::addPairContribution, - &PairQuantityWrap::default_addPairContribution, - (python::arg("bnds"), python::arg("sumscale")), + nb::arg("bnds"), nb::arg("sumscale"), doc_PairQuantity__addPairContribution) .def("_executeParallelMerge", &PairQuantityExposed::executeParallelMerge, - &PairQuantityWrap::default_executeParallelMerge, - python::arg("pdata"), + nb::arg("pdata"), doc_PairQuantity__executeParallelMerge) .def("_finishValue", &PairQuantityExposed::finishValue, - &PairQuantityWrap::default_finishValue, doc_PairQuantity__finishValue) .def("_stashPartialValue", &PairQuantityExposed::stashPartialValue, - &PairQuantityWrap::default_stashPartialValue, doc_PairQuantity__stashPartialValue) .def("_restorePartialValue", &PairQuantityExposed::restorePartialValue, - &PairQuantityWrap::default_restorePartialValue, doc_PairQuantity__restorePartialValue) - .add_property("_value", make_function(&PairQuantityWrap::value, - return_internal_reference<>()), + .def_prop_ro("_value", [](PairQuantityExposed &pq) -> QuantityType & + { + return pq.value(); + }, + nb::rv_policy::reference_internal, doc_PairQuantity__value) + ; // classes PairQuantityExposed, PairQuantityWrap add no members, // therefore we can create pickle suite from C++ base class. - .def_pickle(PairQuantityPickleSuite()) - ; + PairQuantityPickleSuite::bind(pq); } From 57a2b6ccb237b278fdb17a6a49b4cda6609bc07e Mon Sep 17 00:00:00 2001 From: symscae Date: Thu, 25 Jun 2026 13:09:56 -0700 Subject: [PATCH 10/18] migrated bindings for PDFCalculators and PDFenvelope --- src/extensions/srreal_converters.hpp | 3 + src/extensions/wrap_PDFCalculators.cpp | 207 +++++++++++++------------ src/extensions/wrap_PDFEnvelope.cpp | 116 +++++++++----- 3 files changed, 189 insertions(+), 137 deletions(-) diff --git a/src/extensions/srreal_converters.hpp b/src/extensions/srreal_converters.hpp index fb3cad6e..21018a4a 100644 --- a/src/extensions/srreal_converters.hpp +++ b/src/extensions/srreal_converters.hpp @@ -17,6 +17,9 @@ * *****************************************************************************/ +// TODO: Go through type casters, some of which are found redundant or could be +// replaced with nanobind builtins. + #ifndef SRREAL_CONVERTERS_HPP_INCLUDED #define SRREAL_CONVERTERS_HPP_INCLUDED diff --git a/src/extensions/wrap_PDFCalculators.cpp b/src/extensions/wrap_PDFCalculators.cpp index df4cdd22..2393b6ee 100644 --- a/src/extensions/wrap_PDFCalculators.cpp +++ b/src/extensions/wrap_PDFCalculators.cpp @@ -16,9 +16,7 @@ * *****************************************************************************/ -#include -#include -#include +#include #include #include @@ -26,10 +24,11 @@ #include "srreal_converters.hpp" #include "srreal_pickling.hpp" +namespace nb = nanobind; + namespace srrealmodule { namespace nswrap_PDFCalculators { -using namespace boost::python; using namespace diffpy::srreal; // docstrings ---------------------------------------------------------------- @@ -193,36 +192,36 @@ DECLARE_BYTYPE_SETTER_WRAPPER(setBaseline, setbaseline) // wrappers for the envelopes property -PDFEnvelopePtr pyobjtoenvelope(object evp) +PDFEnvelopePtr pyobjtoenvelope(nb::object evp) { - extract tp(evp); - if (tp.check()) return PDFEnvelope::createByType(tp()); - PDFEnvelopePtr rv = extract(evp); - return rv; + std::string tp; + if (nb::try_cast(evp, tp, false)) + { + return PDFEnvelope::createByType(tp); + } + return nb::cast(evp); } template -tuple getenvelopes(T& obj) +nb::tuple getenvelopes(T& obj) { std::set etps = obj.usedEnvelopeTypes(); std::set::const_iterator tpi; - list elst; + nb::list elst; for (tpi = etps.begin(); tpi != etps.end(); ++tpi) { elst.append(obj.getEnvelopeByType(*tpi)); } - tuple rv(elst); - return rv; + return nb::tuple(elst); } template -void setenvelopes(T& obj, object evps) +void setenvelopes(T& obj, nb::object evps) { - stl_input_iterator eit(evps), end; // first check if all evps items can be converted to PDFEnvelopePtr std::list elst; - for (; eit != end; ++eit) elst.push_back(pyobjtoenvelope(*eit)); + for (nb::handle evp : evps) elst.push_back(pyobjtoenvelope(nb::borrow(evp))); // if that worked, overwrite the original envelopes obj.clearEnvelopes(); std::list::iterator eii = elst.begin(); @@ -232,27 +231,30 @@ void setenvelopes(T& obj, object evps) // wrapper for the usedenvelopetypes template -tuple getusedenvelopetypes(T& obj) +nb::tuple getusedenvelopetypes(T& obj) { - tuple rv(usedEnvelopeTypes_aslist(obj)); + nb::tuple rv(usedEnvelopeTypes_aslist(obj)); return rv; } template -void addenvelope(T& obj, object evp) +void addenvelope(T& obj, nb::object evp) { PDFEnvelopePtr e = pyobjtoenvelope(evp); obj.addEnvelope(e); } template -void popenvelope(T& obj, object evp) +void popenvelope(T& obj, nb::object evp) { - extract tp(evp); - if (tp.check()) obj.popEnvelopeByType(tp()); + std::string tp; + if (nb::try_cast(evp, tp, false)) + { + obj.popEnvelopeByType(tp); + } else { - PDFEnvelopePtr e = extract(evp); + PDFEnvelopePtr e = nb::cast(evp); obj.popEnvelope(e); } } @@ -266,88 +268,81 @@ PDFEnvelopePtr getoneenvelope(T& obj, const std::string& tp) // wrap shared methods and attributes of PDFCalculators -template -C& wrap_PDFCommon(C& boostpythonclass) +template +C& wrap_PDFCommon(C& cls) { - namespace bp = boost::python; - typedef typename C::wrapped_type W; - boostpythonclass + cls // result vectors - .add_property("pdf", getPDF_asarray, - doc_PDFCommon_pdf) - .add_property("rdf", getRDF_asarray, - doc_PDFCommon_rdf) - .add_property("rgrid", getRgrid_asarray, - doc_PDFCommon_rgrid) - .add_property("fq", getF_asarray, - doc_PDFCommon_fq) - .add_property("qgrid", getQgrid_asarray, - doc_PDFCommon_qgrid) + .def_prop_ro("pdf", getPDF_asarray, doc_PDFCommon_pdf) + .def_prop_ro("rdf", getRDF_asarray, doc_PDFCommon_rdf) + .def_prop_ro("rgrid", getRgrid_asarray, doc_PDFCommon_rgrid) + .def_prop_ro("fq", getF_asarray, doc_PDFCommon_fq) + .def_prop_ro("qgrid", getQgrid_asarray, doc_PDFCommon_qgrid) // PDF envelopes - .add_property("envelopes", + .def_prop_rw("envelopes", getenvelopes, setenvelopes, doc_PDFCommon_envelopes) - .add_property("usedenvelopetypes", + .def_prop_ro("usedenvelopetypes", getusedenvelopetypes, doc_PDFCommon_usedenvelopetypes) .def("addEnvelope", addenvelope, - bp::arg("envlp"), doc_PDFCommon_addEnvelope) + nb::arg("envlp"), doc_PDFCommon_addEnvelope) .def("popEnvelope", popenvelope, - doc_PDFCommon_popEnvelope) + nb::arg("envlp"), doc_PDFCommon_popEnvelope) .def("getEnvelope", getoneenvelope, - bp::arg("tp"), doc_PDFCommon_getEnvelope) + nb::arg("tp"), doc_PDFCommon_getEnvelope) .def("clearEnvelopes", &W::clearEnvelopes, doc_PDFCommon_clearEnvelopes) ; - return boostpythonclass; + return cls; } // local helper for converting python object to a quantity type -tuple fftftog_array_step(object f, double qstep, double qmin) +nb::tuple fftftog_array_step(nb::object f, double qstep, double qmin) { QuantityType f0; const QuantityType& f1 = extractQuantityType(f, f0); QuantityType g = fftftog(f1, qstep, qmin); - object ga = convertToNumPyArray(g); + nb::object ga = convertToNumPyArray(g); double qmaxpad = g.size() * qstep; double rstep = (qmaxpad > 0) ? (M_PI / qmaxpad) : 0.0; - return make_tuple(ga, rstep); + return nb::make_tuple(ga, rstep); } -tuple fftgtof_array_step(object g, double rstep, double rmin) +nb::tuple fftgtof_array_step(nb::object g, double rstep, double rmin) { QuantityType g0; const QuantityType& g1 = extractQuantityType(g, g0); QuantityType f = fftgtof(g1, rstep, rmin); - object fa = convertToNumPyArray(f); + nb::object fa = convertToNumPyArray(f); double rmaxpad = f.size() * rstep; double qstep = (rmaxpad > 0) ? (M_PI / rmaxpad) : 0.0; - return make_tuple(fa, qstep); + return nb::make_tuple(fa, qstep); } // pickling support ---------------------------------------------------------- template -tuple getstate_super(object& obj) +nb::tuple getstate_super(nb::object obj) { // obtain C++ state without PDFEnvelopes - object envlps = obj.attr("envelopes"); + nb::object envlps = obj.attr("envelopes"); obj.attr("clearEnvelopes")(); - assert(len(obj.attr("envelopes")) == 0); - tuple rv(make_tuple(Super::getstate(obj))); + assert(nb::len(obj.attr("envelopes")) == 0); + nb::tuple super_state = Super::getstate(obj); obj.attr("envelopes") = envlps; - assert(len(obj.attr("envelopes")) == len(envlps)); - return rv; + assert(nb::len(obj.attr("envelopes")) == nb::len(envlps)); + return nb::make_tuple(super_state); } -tuple getstate_common(object& obj) +nb::tuple getstate_common(nb::object obj) { auto resolve_pwm = resolve_state_object; auto resolve_sft = resolve_state_object; - tuple rv = make_tuple( + nb::tuple rv = make_tuple( resolve_pwm(obj.attr("peakwidthmodel")), resolve_sft(obj.attr("scatteringfactortable")), obj.attr("envelopes") @@ -356,13 +351,12 @@ tuple getstate_common(object& obj) } -template -void setstate_common(object& obj, Iter& st) +void setstate_common(nb::object obj, nb::tuple state, size_t& pos) { - assign_state_object(obj.attr("peakwidthmodel"), *(++st)); - assign_state_object(obj.attr("scatteringfactortable"), *(++st)); - assert(len(obj.attr("envelopes")) == 0); - obj.attr("envelopes") = *(++st); + assign_state_object(obj.attr("peakwidthmodel"), state[pos++]); + assign_state_object(obj.attr("scatteringfactortable"), state[pos++]); + assert(nb::len(obj.attr("envelopes")) == 0); + obj.attr("envelopes") = nb::borrow(state[pos++]); } @@ -375,9 +369,19 @@ class DebyePDFCalculatorPickleSuite : public: - static tuple getstate(object obj) + template + static void bind(C& cls) + { + cls + .def("__getstate__", getstate) + .def("__setstate__", setstate) + ; + } + + + static nb::tuple getstate(nb::object obj) { - tuple rv( + nb::tuple rv( getstate_super(obj) + getstate_common(obj) ); @@ -385,15 +389,15 @@ class DebyePDFCalculatorPickleSuite : } - static void setstate(object obj, tuple state) + static void setstate(nb::object obj, nb::tuple state) { ensure_tuple_length(state, 4); // restore the state using boost serialization - tuple st0 = extract(state[0]); + nb::tuple st0(state[0]); Super::setstate(obj, st0); // other items are non-None only when restoring Python class - stl_input_iterator st(state); - setstate_common(obj, st); + size_t pos = 1; + setstate_common(obj, state, pos); } }; @@ -407,13 +411,22 @@ class PDFCalculatorPickleSuite : public: - static tuple getstate(object obj) + template + static void bind(C& cls) + { + cls + .def("__getstate__", getstate) + .def("__setstate__", setstate) + ; + } + + static nb::tuple getstate(nb::object obj) { - tuple mystate = make_tuple( + nb::tuple mystate = make_tuple( resolve_state_object(obj.attr("peakprofile")), resolve_state_object(obj.attr("baseline")) ); - tuple rv( + nb::tuple rv( getstate_super(obj) + getstate_common(obj) + mystate @@ -422,17 +435,17 @@ class PDFCalculatorPickleSuite : } - static void setstate(object obj, tuple state) + static void setstate(nb::object obj, nb::tuple state) { ensure_tuple_length(state, 6); // restore the state using boost serialization - tuple st0 = extract(state[0]); + nb::tuple st0(state[0]); Super::setstate(obj, st0); // other items are non-None only when restoring Python class - stl_input_iterator st(state); - setstate_common(obj, st); - assign_state_object(obj.attr("peakprofile"), *(++st)); - assign_state_object(obj.attr("baseline"), *(++st)); + size_t pos = 1; + setstate_common(obj, state, pos); + assign_state_object(obj.attr("peakprofile"), state[pos++]); + assign_state_object(obj.attr("baseline"), state[pos++]); } }; @@ -440,47 +453,47 @@ class PDFCalculatorPickleSuite : // Wrapper definition -------------------------------------------------------- -void wrap_PDFCalculators() +void wrap_PDFCalculators(nb::module_& m) { using namespace nswrap_PDFCalculators; - namespace bp = boost::python; + // TODO: some types are flattened, we may need to add bindings manually // DebyePDFCalculator - class_ > - dbpdfc_class("DebyePDFCalculator", doc_DebyePDFCalculator); - wrap_PDFCommon(dbpdfc_class) + nb::class_ + dbpdfc_class(m, "DebyePDFCalculator", doc_DebyePDFCalculator); + wrap_PDFCommon(dbpdfc_class) + .def(nb::init<>()) .def("setOptimumQstep", &DebyePDFCalculator::setOptimumQstep, doc_DebyePDFCalculator_setOptimumQstep) .def("isOptimumQstep", &DebyePDFCalculator::isOptimumQstep, doc_DebyePDFCalculator_isOptimumQstep) - .def_pickle(DebyePDFCalculatorPickleSuite()) ; + DebyePDFCalculatorPickleSuite::bind(dbpdfc_class); // PDFCalculator - class_ > - pdfc_class("PDFCalculator", doc_PDFCalculator); - wrap_PDFCommon(pdfc_class) + nb::class_ + pdfc_class(m, "PDFCalculator", doc_PDFCalculator); + wrap_PDFCommon(pdfc_class) + .def(nb::init<>()) // PDF peak profile - .add_property("peakprofile", + .def_prop_rw("peakprofile", getpeakprofile, setpeakprofile, doc_PDFCalculator_peakprofile) // PDF baseline - .add_property("baseline", + .def_prop_rw("baseline", getbaseline, setbaseline, doc_PDFCalculator_baseline) - .def_pickle(PDFCalculatorPickleSuite()) ; + PDFCalculatorPickleSuite::bind(pdfc_class); // FFT functions - def("fftftog", fftftog_array_step, - (bp::arg("f"), bp::arg("qstep"), bp::arg("qmin")=0.0), + m.def("fftftog", fftftog_array_step, + nb::arg("f"), nb::arg("qstep"), nb::arg("qmin") = 0.0, doc_fftftog); - def("fftgtof", fftgtof_array_step, - (bp::arg("g"), bp::arg("rstep"), bp::arg("rmin")=0.0), + m.def("fftgtof", fftgtof_array_step, + nb::arg("g"), nb::arg("rstep"), nb::arg("rmin") = 0.0, doc_fftgtof); } diff --git a/src/extensions/wrap_PDFEnvelope.cpp b/src/extensions/wrap_PDFEnvelope.cpp index 54c9d37e..5e224540 100644 --- a/src/extensions/wrap_PDFEnvelope.cpp +++ b/src/extensions/wrap_PDFEnvelope.cpp @@ -17,10 +17,8 @@ * *****************************************************************************/ -#include -#include -#include -#include +#include +#include #include #include @@ -39,11 +37,11 @@ #include "srreal_pickling.hpp" #include "srreal_registry.hpp" +namespace nb = nanobind; + namespace srrealmodule { namespace nswrap_PDFEnvelope { -using namespace boost; -using namespace boost::python; using namespace diffpy::srreal; // docstrings ---------------------------------------------------------------- @@ -93,43 +91,75 @@ Returns 0 when x > stepcut.\n\ // Helper class allows overload of the PDFEnvelope methods from Python. class PDFEnvelopeWrap : - public PDFEnvelope, - public wrapper_srreal + public PDFEnvelope { public: + NB_TRAMPOLINE(PDFEnvelope, 4); + // HasClassRegistry methods - PDFEnvelopePtr create() const + PDFEnvelopePtr create() const override { - object rv = this->get_pure_virtual_override("create")(); + nb::gil_scoped_acquire gil; + nb::detail::ticket ticket(nb_trampoline, "create", true); + + if (!ticket.key.is_valid()) + { + throw nb::type_error( + "pure virtual method PDFEnvelope.create() called" + ); + } + + nb::object rv = nb_trampoline.base().attr(ticket.key)(); return mconfigurator.fetch(rv); } - PDFEnvelopePtr clone() const + PDFEnvelopePtr clone() const override { - return this->get_pure_virtual_override("clone")(); + NB_OVERRIDE_PURE(clone); } - const std::string& type() const + const std::string& type() const override { - python::object tp = this->get_pure_virtual_override("type")(); - mtype = python::extract(tp); + nb::gil_scoped_acquire gil; + nb::detail::ticket ticket(nb_trampoline, "type", true); + + if (!ticket.key.is_valid()) + { + throw nb::type_error( + "pure virtual method PDFEnvelope.type() called" + ); + } + + nb::object tp = nb_trampoline.base().attr(ticket.key)(); + mtype = nb::cast(tp); return mtype; } // own methods - double operator()(const double& x) const + double operator()(const double& x) const override { - return this->get_pure_virtual_override("__call__")(x); + nb::gil_scoped_acquire gil; + nb::detail::ticket ticket(nb_trampoline, "__call__", true); + + if (!ticket.key.is_valid()) + { + throw nb::type_error( + "pure virtual method PDFEnvelope.__call__() called" + ); + } + + nb::object rv = nb_trampoline.base().attr(ticket.key)(x); + return nb::cast(rv); } protected: // HasClassRegistry method - void setupRegisteredObject(PDFEnvelopePtr p) const + void setupRegisteredObject(PDFEnvelopePtr p) const override { mconfigurator.setup(p); } @@ -151,7 +181,7 @@ class PDFEnvelopeWrap : }; // class PDFEnvelopeWrap -object callnparray(const PDFEnvelope* obj, object& x) +nb::object callnparray(const PDFEnvelope* obj, nb::object& x) { NumPyArray_DoublePtr xx = extractNumPyDoubleArray(x); NumPyArray_DoublePtr yy = createNumPyDoubleArrayLike(xx.first); @@ -166,40 +196,46 @@ object callnparray(const PDFEnvelope* obj, object& x) // Wrapper definition -------------------------------------------------------- -void wrap_PDFEnvelope() +void wrap_PDFEnvelope(nb::module_ &m) { using namespace nswrap_PDFEnvelope; using diffpy::Attributes; - namespace bp = boost::python; - class_, noncopyable> - pdfenvelope("PDFEnvelope", doc_PDFEnvelope); + nb::class_ + pdfenvelope(m, "PDFEnvelope", nb::dynamic_attr(), doc_PDFEnvelope); wrap_registry_methods(pdfenvelope) + .def(nb::init<>()) .def("__call__", callnparray, - bp::arg("r_array")) + nb::arg("r_array")) .def("__call__", &PDFEnvelope::operator(), - bp::arg("r"), doc_PDFEnvelope___call__) - .def_pickle(SerializationPickleSuite()) + nb::arg("r"), doc_PDFEnvelope___call__) ; + SerializationPickleSuite::bind(pdfenvelope); - register_ptr_to_python(); - - class_ >( - "QResolutionEnvelope", doc_QResolutionEnvelope) - .def_pickle(SerializationPickleSuite()) + nb::class_ qresenvelope(m, + "QResolutionEnvelope", doc_QResolutionEnvelope); + qresenvelope + .def(nb::init<>()) ; - class_ >( - "ScaleEnvelope", doc_ScaleEnvelope) - .def_pickle(SerializationPickleSuite()) + SerializationPickleSuite::bind(qresenvelope); + nb::class_ scaleenvelope(m, + "ScaleEnvelope", doc_ScaleEnvelope); + scaleenvelope + .def(nb::init<>()) ; - class_ >( - "SphericalShapeEnvelope", doc_SphericalShapeEnvelope) - .def_pickle(SerializationPickleSuite()) + SerializationPickleSuite::bind(scaleenvelope); + nb::class_ sphshapeenvelope(m, + "SphericalShapeEnvelope", doc_SphericalShapeEnvelope); + sphshapeenvelope + .def(nb::init<>()) ; - class_ >( - "StepCutEnvelope", doc_StepCutEnvelope) - .def_pickle(SerializationPickleSuite()) + SerializationPickleSuite::bind(sphshapeenvelope); + nb::class_ stepcutenvelope(m, + "StepCutEnvelope", doc_StepCutEnvelope); + sphshapeenvelope + .def(nb::init<>()) ; + SerializationPickleSuite::bind(sphshapeenvelope); } From d7ce20889938f0db528b4ba8245b51560fba8da5 Mon Sep 17 00:00:00 2001 From: symscae Date: Thu, 25 Jun 2026 14:53:44 -0700 Subject: [PATCH 11/18] migrated bindings for PeakProfile, PeakWidthModel and ScatteringFactorTable --- src/extensions/wrap_PeakProfile.cpp | 111 ++++++---- src/extensions/wrap_PeakWidthModel.cpp | 128 +++++++----- src/extensions/wrap_ScatteringFactorTable.cpp | 194 +++++++++++------- 3 files changed, 262 insertions(+), 171 deletions(-) diff --git a/src/extensions/wrap_PeakProfile.cpp b/src/extensions/wrap_PeakProfile.cpp index ac0c1ce1..e59656e6 100644 --- a/src/extensions/wrap_PeakProfile.cpp +++ b/src/extensions/wrap_PeakProfile.cpp @@ -17,10 +17,8 @@ * *****************************************************************************/ -#include -#include -#include -#include +#include +#include #include @@ -35,8 +33,6 @@ namespace srrealmodule { namespace nswrap_PeakProfile { -using namespace boost; -using namespace boost::python; using namespace diffpy::srreal; // docstrings ---------------------------------------------------------------- @@ -110,61 +106,84 @@ The profile is also rescaled to keep the integrated area of 1.\n\ // Support for override of PeakProfile methods from Python. class PeakProfileWrap : - public PeakProfile, - public wrapper_srreal + public PeakProfile { public: + NB_TRAMPOLINE(PeakProfile, 7); + // HasClassRegistry methods - PeakProfilePtr create() const + PeakProfilePtr create() const override { - object rv = this->get_pure_virtual_override("create")(); + nb::gil_scoped_acquire gil; + nb::detail::ticket ticket(nb_trampoline, "create", true); + + if (!ticket.key.is_valid()) + { + throw nb::type_error( + "pure virtual method PeakProfile.create() called" + ); + } + + nb::object rv = nb_trampoline.base().attr(ticket.key)(); return mconfigurator.fetch(rv); } - PeakProfilePtr clone() const + PeakProfilePtr clone() const override { - return this->get_pure_virtual_override("clone")(); + NB_OVERRIDE_PURE(clone); } - const std::string& type() const + const std::string& type() const override { - python::object tp = this->get_pure_virtual_override("type")(); - mtype = python::extract(tp); + nb::gil_scoped_acquire gil; + nb::detail::ticket ticket(nb_trampoline, "type", true); + + if (!ticket.key.is_valid()) + { + throw nb::type_error( + "pure virtual method PeakProfile.type() called" + ); + } + + nb::object tp = nb_trampoline.base().attr(ticket.key)(); + mtype = nb::cast(tp); return mtype; } // own methods - double operator()(double x, double fwhm) const + double operator()(double x, double fwhm) const override { - return this->get_pure_virtual_override("__call__")(x, fwhm); + NB_OVERRIDE_PURE_NAME("__call__", operator(), x, fwhm); } - double xboundlo(double fwhm) const + double xboundlo(double fwhm) const override { - return this->get_pure_virtual_override("xboundlo")(fwhm); + NB_OVERRIDE_PURE(xboundlo, fwhm); } - double xboundhi(double fwhm) const + double xboundhi(double fwhm) const override { - return this->get_pure_virtual_override("xboundhi")(fwhm); + NB_OVERRIDE_PURE(xboundhi, fwhm); } // Support for ticker override from Python. - diffpy::eventticker::EventTicker& ticker() const + diffpy::eventticker::EventTicker& ticker() const override { using diffpy::eventticker::EventTicker; - override f = this->get_override("ticker"); - if (f) + nb::gil_scoped_acquire gil; + nb::detail::ticket ticket(nb_trampoline, "ticker", false); + + if (ticket.key.is_valid()) { - // avoid "dangling reference error" when used from C++ - python::object ptic = f(); - return python::extract(ptic); + nb::object ptic = nb_trampoline.base().attr(ticket.key)(); + return nb::cast(ptic); } + return this->default_ticker(); } @@ -177,7 +196,7 @@ class PeakProfileWrap : // HasClassRegistry method - void setupRegisteredObject(PeakProfilePtr p) const + void setupRegisteredObject(PeakProfilePtr p) const override { mconfigurator.setup(p); } @@ -202,40 +221,42 @@ class PeakProfileWrap : // Wrapper definition -------------------------------------------------------- -void wrap_PeakProfile() +void wrap_PeakProfile(nb::module_& m) { using namespace nswrap_PeakProfile; using diffpy::Attributes; - namespace bp = boost::python; - class_, noncopyable> - peakprofile("PeakProfile", doc_PeakProfile); + nb::class_ + peakprofile(m, "PeakProfile", nb::dynamic_attr(), doc_PeakProfile); wrap_registry_methods(peakprofile) + .def(nb::init<>()) .def("__call__", &PeakProfile::operator(), - (bp::arg("x"), bp::arg("fwhm")), doc_PeakProfile___call__) + nb::arg("x"), nb::arg("fwhm"), doc_PeakProfile___call__) .def("xboundlo", &PeakProfile::xboundlo, - bp::arg("fwhm"), doc_PeakProfile_xboundlo) + nb::arg("fwhm"), doc_PeakProfile_xboundlo) .def("xboundhi", &PeakProfile::xboundhi, - bp::arg("fwhm"), doc_PeakProfile_xboundhi) + nb::arg("fwhm"), doc_PeakProfile_xboundhi) .def("ticker", &PeakProfile::ticker, - &PeakProfileWrap::default_ticker, - return_internal_reference<>(), + nb::rv_policy::reference_internal, doc_PeakProfile_ticker) - .def_pickle(SerializationPickleSuite()) ; - register_ptr_to_python(); + SerializationPickleSuite::bind(peakprofile); - class_ >( - "GaussianProfile", doc_GaussianProfile) - .def_pickle(SerializationPickleSuite()) + nb::class_ gaussianprofile(m, + "GaussianProfile", doc_GaussianProfile); + gaussianprofile + .def(nb::init<>()) ; + SerializationPickleSuite::bind(gaussianprofile); - class_ >( - "CroppedGaussianProfile", doc_CroppedGaussianProfile) - .def_pickle(SerializationPickleSuite()) + nb::class_ croppedgaussianprofile(m, + "CroppedGaussianProfile", doc_CroppedGaussianProfile); + croppedgaussianprofile + .def(nb::init<>()) ; + SerializationPickleSuite::bind(croppedgaussianprofile); } diff --git a/src/extensions/wrap_PeakWidthModel.cpp b/src/extensions/wrap_PeakWidthModel.cpp index 21bfac74..69924f31 100644 --- a/src/extensions/wrap_PeakWidthModel.cpp +++ b/src/extensions/wrap_PeakWidthModel.cpp @@ -17,11 +17,8 @@ * *****************************************************************************/ -#include -#include -#include -#include -#include +#include +#include #include @@ -35,11 +32,11 @@ #include "srreal_pickling.hpp" #include "srreal_registry.hpp" +namespace nb = nanobind; + namespace srrealmodule { namespace nswrap_PeakWidthModel { -using namespace boost; -using namespace boost::python; using namespace diffpy::srreal; // docstrings ---------------------------------------------------------------- @@ -110,7 +107,7 @@ Use PeakWidthModel.getRegisteredTypes() for a set of registered names.\n\ // wrappers ------------------------------------------------------------------ double maxwidthwithpystructure(const PeakWidthModel& pwm, - python::object stru, double rmin, double rmax) + nb::object stru, double rmin, double rmax) { StructureAdapterPtr adpt = createStructureAdapter(stru); double rv = pwm.maxWidth(adpt, rmin, rmax); @@ -129,57 +126,79 @@ DECLARE_BYTYPE_SETTER_WRAPPER(setPeakWidthModel, setpwmodel) // Support for Python override of the PeakWidthModel methods. class PeakWidthModelWrap : - public PeakWidthModel, - public wrapper_srreal + public PeakWidthModel { public: + NB_TRAMPOLINE(PeakWidthModel, 6); + // HasClassRegistry methods - PeakWidthModelPtr create() const + PeakWidthModelPtr create() const override { - object rv = this->get_pure_virtual_override("create")(); + nb::gil_scoped_acquire gil; + nb::detail::ticket ticket(nb_trampoline, "create", true); + + if (!ticket.key.is_valid()) + { + throw nb::type_error( + "pure virtual method PeakWidthModel.create() called" + ); + } + + nb::object rv = nb_trampoline.base().attr(ticket.key)(); return mconfigurator.fetch(rv); } - PeakWidthModelPtr clone() const + PeakWidthModelPtr clone() const override { - return this->get_pure_virtual_override("clone")(); + NB_OVERRIDE_PURE(clone); } - const std::string& type() const + const std::string& type() const override { - python::object tp = this->get_pure_virtual_override("type")(); - mtype = python::extract(tp); + nb::gil_scoped_acquire gil; + nb::detail::ticket ticket(nb_trampoline, "type", true); + + if (!ticket.key.is_valid()) + { + throw nb::type_error( + "pure virtual method PeakWidthModel.type() called" + ); + } + + nb::object tp = nb_trampoline.base().attr(ticket.key)(); + mtype = nb::cast(tp); return mtype; } // own methods - double calculate(const BaseBondGenerator& bnds) const + double calculate(const BaseBondGenerator& bnds) const override { - return this->get_pure_virtual_override("calculate")(ptr(&bnds)); + NB_OVERRIDE_PURE(calculate, bnds); } double maxWidth(StructureAdapterPtr stru, - double rmin, double rmax) const + double rmin, double rmax) const override { - override f = this->get_pure_virtual_override("maxWidth"); - return f(stru, rmin, rmax); + NB_OVERRIDE_PURE(maxWidth, stru, rmin, rmax); } // Make the ticker method overridable from Python - diffpy::eventticker::EventTicker& ticker() const + diffpy::eventticker::EventTicker& ticker() const override { using diffpy::eventticker::EventTicker; - override f = this->get_override("ticker"); - if (f) + nb::gil_scoped_acquire gil; + nb::detail::ticket ticket(nb_trampoline, "ticker", false); + + if (ticket.key.is_valid()) { - // avoid "dangling reference error" when used from C++ - python::object ptic = f(); - return python::extract(ptic); + nb::object ptic = nb_trampoline.base().attr(ticket.key)(); + return nb::cast(ptic); } + return this->default_ticker(); } @@ -192,7 +211,7 @@ class PeakWidthModelWrap : // HasClassRegistry method - void setupRegisteredObject(PeakWidthModelPtr p) const + void setupRegisteredObject(PeakWidthModelPtr p) const override { mconfigurator.setup(p); } @@ -217,53 +236,56 @@ class PeakWidthModelWrap : // Wrapper definition -------------------------------------------------------- -void wrap_PeakWidthModel() +void wrap_PeakWidthModel(nb::module_& m) { - namespace bp = boost::python; using namespace nswrap_PeakWidthModel; using diffpy::Attributes; - class_, noncopyable> - peakwidthmodel("PeakWidthModel", doc_PeakWidthModel); + nb::class_ + peakwidthmodel(m, "PeakWidthModel", nb::dynamic_attr(), doc_PeakWidthModel); wrap_registry_methods(peakwidthmodel) + .def(nb::init<>()) .def("calculate", &PeakWidthModel::calculate, - bp::arg("bnds"), + nb::arg("bnds"), doc_PeakWidthModel_calculate) .def("maxWidth", &PeakWidthModel::maxWidth, - (bp::arg("stru"), bp::arg("rmin"), bp::arg("rmax")), + nb::arg("stru"), nb::arg("rmin"), nb::arg("rmax"), doc_PeakWidthModel_maxWidth) .def("maxWidth", maxwidthwithpystructure, - (bp::arg("stru"), bp::arg("rmin"), bp::arg("rmax"))) + nb::arg("stru"), nb::arg("rmin"), nb::arg("rmax")) .def("ticker", &PeakWidthModel::ticker, - &PeakWidthModelWrap::default_ticker, - return_internal_reference<>(), + nb::rv_policy::reference_internal, doc_PeakWidthModel_ticker) - .def_pickle(SerializationPickleSuite()) ; + SerializationPickleSuite::bind(peakwidthmodel); - register_ptr_to_python(); - - class_ >( - "ConstantPeakWidth", doc_ConstantPeakWidth) - .def_pickle(SerializationPickleSuite()) + nb::class_ constantpeakwidth(m, + "ConstantPeakWidth", doc_ConstantPeakWidth); + constantpeakwidth + .def(nb::init<>()) ; - - class_ >( - "DebyeWallerPeakWidth", doc_DebyeWallerPeakWidth) - .def_pickle(SerializationPickleSuite()) + SerializationPickleSuite::bind(constantpeakwidth); + + nb::class_ debywallerpeakwidth(m, + "DebyeWallerPeakWidth", doc_DebyeWallerPeakWidth); + debywallerpeakwidth + .def(nb::init<>()) ; + SerializationPickleSuite::bind(debywallerpeakwidth); - class_ >( - "JeongPeakWidth", doc_JeongPeakWidth) - .def_pickle(SerializationPickleSuite()) + nb::class_ jeongpeakwidth(m, + "JeongPeakWidth", doc_JeongPeakWidth); + jeongpeakwidth + .def(nb::init<>()) ; + SerializationPickleSuite::bind(jeongpeakwidth); - class_("PeakWidthModelOwner", doc_PeakWidthModelOwner) - .add_property("peakwidthmodel", + nb::class_(m, "PeakWidthModelOwner", doc_PeakWidthModelOwner) + .def_prop_rw("peakwidthmodel", getpwmodel, setpwmodel, doc_PeakWidthModelOwner_peakwidthmodel) diff --git a/src/extensions/wrap_ScatteringFactorTable.cpp b/src/extensions/wrap_ScatteringFactorTable.cpp index d7e1875c..db9e2ba9 100644 --- a/src/extensions/wrap_ScatteringFactorTable.cpp +++ b/src/extensions/wrap_ScatteringFactorTable.cpp @@ -17,9 +17,8 @@ * *****************************************************************************/ -#include -#include -#include +#include +#include #include #include @@ -39,10 +38,11 @@ #include "srreal_pickling.hpp" #include "srreal_registry.hpp" +namespace nb = nanobind; + namespace srrealmodule { namespace nswrap_ScatteringFactorTable { -using namespace boost::python; using namespace diffpy::srreal; // docstrings @@ -211,11 +211,12 @@ DECLARE_BYTYPE_SETTER_WRAPPER(setScatteringFactorTable, setsftable) // Helper class to support Python override of ScatteringFactorTable methods class ScatteringFactorTableWrap : - public ScatteringFactorTable, - public wrapper_srreal + public ScatteringFactorTable { public: + NB_TRAMPOLINE(ScatteringFactorTable, 6); + // Copy Constructor ScatteringFactorTableWrap() { } @@ -230,36 +231,66 @@ class ScatteringFactorTableWrap : // HasClassRegistry methods - ScatteringFactorTablePtr create() const + ScatteringFactorTablePtr create() const override { - object rv = this->get_pure_virtual_override("create")(); + nb::gil_scoped_acquire gil; + nb::detail::ticket ticket(nb_trampoline, "create", true); + + if (!ticket.key.is_valid()) + { + throw nb::type_error( + "pure virtual method ScatteringFactorTable.create() called" + ); + } + + nb::object rv = nb_trampoline.base().attr(ticket.key)(); return mconfigurator.fetch(rv); } - ScatteringFactorTablePtr clone() const + ScatteringFactorTablePtr clone() const override { - return this->get_pure_virtual_override("clone")(); + NB_OVERRIDE_PURE(clone); } - const std::string& type() const + const std::string& type() const override { - object tp = this->get_pure_virtual_override("type")(); - mtype = extract(tp); + nb::gil_scoped_acquire gil; + nb::detail::ticket ticket(nb_trampoline, "type", true); + + if (!ticket.key.is_valid()) + { + throw nb::type_error( + "pure virtual method ScatteringFactorTable.type() called" + ); + } + + nb::object tp = nb_trampoline.base().attr(ticket.key)(); + mtype = nb::cast(tp); return mtype; } // own methods - const std::string& radiationType() const + const std::string& radiationType() const override { - object tp = this->get_pure_virtual_override("radiationType")(); - mradiationtype = extract(tp); + nb::gil_scoped_acquire gil; + nb::detail::ticket ticket(nb_trampoline, "radiationType", true); + + if (!ticket.key.is_valid()) + { + throw nb::type_error( + "pure virtual method ScatteringFactorTable.radiationType() called" + ); + } + + nb::object tp = nb_trampoline.base().attr(ticket.key)(); + mradiationtype = nb::cast(tp); return mradiationtype; } - double standardLookup(const std::string& smbl, double q) const + double standardLookup(const std::string& smbl, double q) const override { - return this->get_pure_virtual_override("_standardLookup")(smbl, q); + NB_OVERRIDE_PURE_NAME("_standardLookup", standardLookup, smbl, q); } @@ -268,13 +299,15 @@ class ScatteringFactorTableWrap : diffpy::eventticker::EventTicker& ticker() const { using diffpy::eventticker::EventTicker; - override f = this->get_override("ticker"); - if (f) + nb::gil_scoped_acquire gil; + nb::detail::ticket ticket(nb_trampoline, "ticker", false); + + if (ticket.key.is_valid()) { - // avoid "dangling reference error" when used from C++ - object ptic = f(); - return extract(ptic); + nb::object ptic = nb_trampoline.base().attr(ticket.key)(); + return nb::cast(ptic); } + return this->default_ticker(); } @@ -287,7 +320,7 @@ class ScatteringFactorTableWrap : // HasClassRegistry method - void setupRegisteredObject(ScatteringFactorTablePtr p) const + void setupRegisteredObject(ScatteringFactorTablePtr p) const override { mconfigurator.setup(p); } @@ -309,8 +342,8 @@ class ScatteringFactorTableWrap : }; // class ScatteringFactorTableWrap -object lookupnparray(const ScatteringFactorTable& sftb, - std::string smbl, object& qobj) +nb::object lookupnparray(const ScatteringFactorTable& sftb, + std::string smbl, nb::object& qobj) { NumPyArray_DoublePtr aa = extractNumPyDoubleArray(qobj); NumPyArray_DoublePtr bb = createNumPyDoubleArrayLike(aa.first); @@ -328,77 +361,91 @@ object lookupnparray(const ScatteringFactorTable& sftb, } // namespace nswrap_ScatteringFactorTable // Wrapper definition -------------------------------------------------------- +// TODO: rework pickle helpers -void wrap_ScatteringFactorTable() +void wrap_ScatteringFactorTable(nb::module_& m) { - namespace bp = boost::python; - using boost::noncopyable; using namespace nswrap_ScatteringFactorTable; typedef ScatteringFactorTableOwner SFTOwner; - class_ - sftb("ScatteringFactorTable", doc_ScatteringFactorTable); + nb::class_ + sftb(m, "ScatteringFactorTable", nb::dynamic_attr(), doc_ScatteringFactorTable); wrap_registry_methods(sftb) + .def(nb::init<>()) .def("radiationType", - &ScatteringFactorTable::radiationType, - return_value_policy(), + [](const ScatteringFactorTable &obj) + { + return std::string(obj.radiationType()); + }, doc_ScatteringFactorTable_radiationType) .def("lookup", lookupnparray, - (bp::arg("smbl"), bp::arg("qarray"))) + nb::arg("smbl"), nb::arg("qarray")) .def("lookup", &ScatteringFactorTable::lookup, - (bp::arg("smbl"), bp::arg("q")=0.0), + nb::arg("smbl"), nb::arg("q")=0.0, doc_ScatteringFactorTable_lookup) .def("_standardLookup", &ScatteringFactorTable::standardLookup, - (bp::arg("smbl"), bp::arg("q")), + nb::arg("smbl"), nb::arg("q"), doc_ScatteringFactorTable__standardLookup) .def("setCustomAs", (void (ScatteringFactorTable::*) (const std::string&, const std::string&)) &ScatteringFactorTable::setCustomAs, - (bp::arg("smbl"), bp::arg("src")), + nb::arg("smbl"), nb::arg("src"), doc_ScatteringFactorTable_setCustomAs2) .def("setCustomAs", (void (ScatteringFactorTable::*) (const std::string&, const std::string&, double, double)) &ScatteringFactorTable::setCustomAs, - (bp::arg("smbl"), bp::arg("src"), - bp::arg("sf"), bp::arg("q")=0.0), + nb::arg("smbl"), nb::arg("src"), + nb::arg("sf"), nb::arg("q")=0.0, doc_ScatteringFactorTable_setCustomAs4) .def("resetCustom", &ScatteringFactorTable::resetCustom, - bp::arg("smbl"), doc_ScatteringFactorTable_resetCustom) + nb::arg("smbl"), doc_ScatteringFactorTable_resetCustom) .def("resetAll", &ScatteringFactorTable::resetAll, doc_ScatteringFactorTable_resetAll) .def("getCustomSymbols", getCustomSymbols_asset, doc_ScatteringFactorTable_getCustomSymbols) .def("ticker", &ScatteringFactorTable::ticker, - &ScatteringFactorTableWrap::default_ticker, - return_internal_reference<>(), + nb::rv_policy::reference_internal, doc_ScatteringFactorTable_ticker) - .def_pickle(SerializationPickleSuite()) ; + SerializationPickleSuite::bind(sftb); - register_ptr_to_python(); - - class_ >( - "SFTXray", doc_SFTXray) - .def_pickle(SerializationPickleSuite()); - class_ >( - "SFTElectron", doc_SFTElectron) - .def_pickle(SerializationPickleSuite()); - class_ >( - "SFTNeutron", doc_SFTNeutron) - .def_pickle(SerializationPickleSuite()); - class_ >( - "SFTElectronNumber", doc_SFTElectronNumber) - .def_pickle(SerializationPickleSuite()); - - class_("ScatteringFactorTableOwner", + nb::class_ sftxray(m, + "SFTXray", doc_SFTXray); + sftxray + .def(nb::init<>()) + ; + SerializationPickleSuite::bind(sftxray); + + nb::class_ sftelectron(m, + "SFTElectron", doc_SFTElectron); + sftelectron + .def(nb::init<>()) + ; + SerializationPickleSuite::bind(sftelectron); + + nb::class_ sftneutron(m, + "SFTNeutron", doc_SFTNeutron); + sftneutron + .def(nb::init<>()) + ; + SerializationPickleSuite::bind(sftneutron); + + nb::class_ sftelectronnumber(m, + "SFTElectronNumber", doc_SFTElectronNumber); + sftelectronnumber + .def(nb::init<>()) + ; + SerializationPickleSuite::bind(sftelectronnumber); + + nb::class_(m, "ScatteringFactorTableOwner", doc_ScatteringFactorTableOwner) - .add_property("scatteringfactortable", + .def_prop_rw("scatteringfactortable", getsftable, setsftable, doc_ScatteringFactorTableOwner_scatteringfactortable) @@ -406,27 +453,28 @@ void wrap_ScatteringFactorTable() .def("setScatteringFactorTableByType", +[](SFTOwner& obj, const std::string& tp) { - namespace bp = boost::python; try { - bp::object warnings = bp::import("warnings"); - bp::object builtins = bp::import("builtins"); - bp::object DeprecationWarning = builtins.attr("DeprecationWarning"); - warnings.attr("warn")( - std::string("setScatteringFactorTableByType is deprecated; " - "assign the 'scatteringfactortable' property directly, for example:\n" - "obj.scatteringfactortable = SFTNeutron()"), - DeprecationWarning, - 2); + nb::object warnings = nb::module_::import_("warnings"); + nb::object builtins = nb::module_::import_("builtins"); + nb::object DeprecationWarning = builtins.attr("DeprecationWarning"); + warnings.attr("warn")( + std::string("setScatteringFactorTableByType is deprecated; " + "assign the 'scatteringfactortable' property directly, for example:\n" + "obj.scatteringfactortable = SFTNeutron()"), + DeprecationWarning, + 2); } catch (...) { /* don't let warnings break the binding */ } obj.setScatteringFactorTableByType(tp); }, - bp::arg("tp"), + nb::arg("tp"), doc_ScatteringFactorTableOwner_setScatteringFactorTableByType) .def("getRadiationType", - &SFTOwner::getRadiationType, - return_value_policy(), + [](const SFTOwner &obj) + { + return std::string(obj.getRadiationType()); + }, doc_ScatteringFactorTableOwner_getRadiationType) ; } From 33cc45d5421970dfbe30d7ace36e1094795082ac Mon Sep 17 00:00:00 2001 From: symscae Date: Thu, 25 Jun 2026 15:34:15 -0700 Subject: [PATCH 12/18] migrated bindings for StructureAdapter and StructureDifference --- src/extensions/wrap_StructureAdapter.cpp | 215 ++++++++++---------- src/extensions/wrap_StructureDifference.cpp | 86 ++++---- 2 files changed, 152 insertions(+), 149 deletions(-) diff --git a/src/extensions/wrap_StructureAdapter.cpp b/src/extensions/wrap_StructureAdapter.cpp index 170737f0..d3a63811 100644 --- a/src/extensions/wrap_StructureAdapter.cpp +++ b/src/extensions/wrap_StructureAdapter.cpp @@ -18,11 +18,9 @@ * *****************************************************************************/ -#include -#include -#include -#include -#include +#include +#include +#include #include "srreal_converters.hpp" #include "srreal_pickling.hpp" @@ -33,15 +31,15 @@ #include #include +namespace nb = nanobind; + namespace srrealmodule { // declarations -void sync_StructureDifference(boost::python::object obj); +void sync_StructureDifference(nb::object obj); namespace nswrap_StructureAdapter { -using namespace boost; -using namespace boost::python; using namespace diffpy::srreal; // docstrings ---------------------------------------------------------------- @@ -223,7 +221,7 @@ const std::string& siteAtomType_safe(const StructureAdapter& adpt, int i) DECLARE_PYARRAY_METHOD_WRAPPER1(siteCartesianPosition, siteCartesianPosition_asarray) -python::object siteCartesianPosition_safe(const StructureAdapter& adpt, int i) +nb::object siteCartesianPosition_safe(const StructureAdapter& adpt, int i) { checkindex(adpt, i); return siteCartesianPosition_asarray(adpt, i); @@ -254,7 +252,7 @@ bool siteAnisotropy_safe(const StructureAdapter& adpt, int i) DECLARE_PYARRAY_METHOD_WRAPPER1(siteCartesianUij, siteCartesianUij_asarray) -python::object siteCartesianUij_safe(const StructureAdapter& adpt, int i) +nb::object siteCartesianUij_safe(const StructureAdapter& adpt, int i) { checkindex(adpt, i); return siteCartesianUij_asarray(adpt, i); @@ -263,33 +261,34 @@ python::object siteCartesianUij_safe(const StructureAdapter& adpt, int i) // Helper class necessary for wrapping a pure virtual methods class StructureAdapterWrap : - public StructureAdapter, - public wrapper_srreal + public StructureAdapter { public: - StructureAdapterPtr clone() const + NB_TRAMPOLINE(StructureAdapter, 13); + + StructureAdapterPtr clone() const override { - return this->get_pure_virtual_override("clone")(); + NB_OVERRIDE_PURE(clone); } - BaseBondGeneratorPtr createBondGenerator() const + BaseBondGeneratorPtr createBondGenerator() const override { - return this->get_pure_virtual_override("createBondGenerator")(); + NB_OVERRIDE_PURE(createBondGenerator); } - int countSites() const + int countSites() const override { - return this->get_pure_virtual_override("countSites")(); + NB_OVERRIDE_PURE(countSites); } + // TODO: totalOccupancy is not marked as virtual function + // need to check behaviour double totalOccupancy() const { - override f = this->get_override("totalOccupancy"); - if (f) return f(); return this->default_totalOccupancy(); } @@ -299,11 +298,9 @@ class StructureAdapterWrap : } - double numberDensity() const + double numberDensity() const override { - override f = this->get_override("numberDensity"); - if (f) return f(); - return this->default_numberDensity(); + NB_OVERRIDE(numberDensity); } double default_numberDensity() const @@ -312,16 +309,19 @@ class StructureAdapterWrap : } - const std::string& siteAtomType(int idx) const + const std::string& siteAtomType(int idx) const override { static std::string rv; - override f = this->get_override("siteAtomType"); - if (f) + nb::gil_scoped_acquire gil; + nb::detail::ticket ticket(nb_trampoline, "siteAtomType", false); + + if (ticket.key.is_valid()) { - python::object atp = f(idx); - rv = python::extract(atp); + nb::object atp = nb_trampoline.base().attr(ticket.key)(idx); + rv = nb::cast(atp); return rv; } + return this->default_siteAtomType(idx); } @@ -331,24 +331,31 @@ class StructureAdapterWrap : } - const R3::Vector& siteCartesianPosition(int idx) const + const R3::Vector& siteCartesianPosition(int idx) const override { static R3::Vector rv; - python::object pos = - this->get_pure_virtual_override("siteCartesianPosition")(idx); + nb::gil_scoped_acquire gil; + nb::detail::ticket ticket(nb_trampoline, "siteCartesianPosition", true); + + if (!ticket.key.is_valid()) + { + throw nb::type_error( + "pure virtual method StructureAdapter.siteCartesianPosition() called" + ); + } + + nb::object pos = nb_trampoline.base().attr(ticket.key)(idx); for (int i = 0; i < R3::Ndim; ++i) { - rv[i] = python::extract(pos[i]); + rv[i] = nb::cast(pos[i]); } return rv; } - int siteMultiplicity(int idx) const + int siteMultiplicity(int idx) const override { - override f = this->get_override("siteMultiplicity"); - if (f) return f(idx); - return this->default_siteMultiplicity(idx); + NB_OVERRIDE(siteMultiplicity, idx); } int default_siteMultiplicity(int idx) const @@ -357,11 +364,9 @@ class StructureAdapterWrap : } - double siteOccupancy(int idx) const + double siteOccupancy(int idx) const override { - override f = this->get_override("siteOccupancy"); - if (f) return f(idx); - return this->default_siteOccupancy(idx); + NB_OVERRIDE(siteOccupancy, idx); } double default_siteOccupancy(int idx) const @@ -370,33 +375,40 @@ class StructureAdapterWrap : } - bool siteAnisotropy(int idx) const + bool siteAnisotropy(int idx) const override { - return this->get_pure_virtual_override("siteAnisotropy")(idx); + NB_OVERRIDE_PURE(siteAnisotropy, idx); } - const R3::Matrix& siteCartesianUij(int idx) const + const R3::Matrix& siteCartesianUij(int idx) const override { static R3::Matrix rv; - python::object uij = - this->get_pure_virtual_override("siteCartesianUij")(idx); + nb::gil_scoped_acquire gil; + nb::detail::ticket ticket(nb_trampoline, "siteCartesianUij", true); + + if (!ticket.key.is_valid()) + { + throw nb::type_error( + "pure virtual method StructureAdapter.siteCartesianUij() called" + ); + } + + nb::object uij = nb_trampoline.base().attr(ticket.key)(idx); for (int i = 0; i < R3::Ndim; ++i) { for (int j = 0; j < R3::Ndim; ++j) { - rv(i, j) = python::extract(uij[i][j]); + rv(i, j) = nb::cast(uij[i][j]); } } return rv; } - void customPQConfig(PairQuantity* pq) const + void customPQConfig(PairQuantity* pq) const override { - override f = this->get_override("_customPQConfig"); - if (f) f(ptr(pq)); - else this->default_customPQConfig(pq); + NB_OVERRIDE_NAME("_customPQConfig", customPQConfig, pq); } void default_customPQConfig(PairQuantity* pq) const @@ -405,17 +417,19 @@ class StructureAdapterWrap : } - StructureDifference diff(StructureAdapterConstPtr other) const + StructureDifference diff(StructureAdapterConstPtr other) const override { - override f = this->get_override("diff"); - if (f) - { - python::object sdobj = f(other); + nb::gil_scoped_acquire gil; + nb::detail::ticket ticket(nb_trampoline, "diff", false); + + if (ticket.key.is_valid()) { + nb::object sdobj = nb_trampoline.base().attr(ticket.key)(other); sync_StructureDifference(sdobj); - StructureDifference& sd = - python::extract(sdobj); + StructureDifference &sd = + nb::cast(sdobj); return sd; } + return this->default_diff(other); } @@ -438,18 +452,24 @@ class StructureAdapterWrap : template -class StructureProxyPickleSuite : public boost::python::pickle_suite +class StructureProxyPickleSuite { public: - static boost::python::tuple getinitargs( - diffpy::srreal::StructureAdapterPtr adpt) + template + static void bind(C& cls) { - using namespace boost; - std::shared_ptr tadpt = std::dynamic_pointer_cast(adpt); - StructureAdapterPtr srcadpt = tadpt->getSourceStructure(); - python::tuple rv = python::make_tuple(srcadpt); - return rv; + cls + .def("__reduce__", [](const T& self) + { + StructureAdapterPtr src = self.getSourceStructure(); + + return nb::make_tuple( + nb::type(), + nb::make_tuple(src) + ); + }) + ; } }; @@ -465,7 +485,7 @@ void checkindex(const StructureAdapter& adpt, int i) // Prevent out-of-bounds crash from C++ objects. if (0 <= i && i < adpt.countSites()) return; PyErr_SetString(PyExc_IndexError, "Index out of range."); - throw_error_already_set(); + throw nb::python_error(); } } // namespace @@ -475,13 +495,15 @@ void checkindex(const StructureAdapter& adpt, int i) // Wrapper definition -------------------------------------------------------- -void wrap_StructureAdapter() +void wrap_StructureAdapter(nb::module_& m) { using namespace nswrap_StructureAdapter; - class_( - "StructureAdapter", doc_StructureAdapter) - .def("__init__", StructureAdapter_constructor(), + nb::class_ structureadapter(m, + "StructureAdapter", doc_StructureAdapter); + structureadapter + .def(nb::init<>()) + .def("__init__", StructureAdapter_constructor, doc_StructureAdapter___init__fromstring) .def("clone", &StructureAdapter::clone, @@ -493,31 +515,21 @@ void wrap_StructureAdapter() doc_StructureAdapter_countSites) .def("totalOccupancy", &StructureAdapter::totalOccupancy, - &StructureAdapterWrap::default_totalOccupancy, doc_StructureAdapter_totalOccupancy) .def("numberDensity", &StructureAdapter::numberDensity, - &StructureAdapterWrap::default_numberDensity, doc_StructureAdapter_numberDensity) .def("siteAtomType", siteAtomType_safe, - return_value_policy(), doc_StructureAdapter_siteAtomType) - .def("siteAtomType", - &StructureAdapterWrap::default_siteAtomType, - return_value_policy()) .def("siteCartesianPosition", siteCartesianPosition_safe, doc_StructureAdapter_siteCartesianPosition) .def("siteMultiplicity", siteMultiplicity_safe, doc_StructureAdapter_siteMultiplicity) - .def("siteMultiplicity", - &StructureAdapterWrap::default_siteMultiplicity) .def("siteOccupancy", siteOccupancy_safe, doc_StructureAdapter_siteOccupancy) - .def("siteOccupancy", - &StructureAdapterWrap::default_siteOccupancy) .def("siteAnisotropy", siteAnisotropy_safe, doc_StructureAdapter_siteAnisotropy) @@ -526,43 +538,38 @@ void wrap_StructureAdapter() doc_StructureAdapter_siteCartesianUij) .def("_customPQConfig", &StructureAdapter::customPQConfig, - &StructureAdapterWrap::default_customPQConfig, - python::arg("pqobj"), + nb::arg("pqobj"), doc_StructureAdapter__customPQConfig) .def("diff", &StructureAdapter::diff, - &StructureAdapterWrap::default_diff, - python::arg("other"), + nb::arg("other"), doc_StructureAdapter_diff) - .def_pickle(StructureAdapterPickleSuite()) ; - - register_ptr_to_python(); - implicitly_convertible(); + StructureProxyPickleSuite::bind(structureadapter); typedef std::shared_ptr NoMetaStructureAdapterPtr; - class_, NoMetaStructureAdapterPtr>( - "NoMetaStructureAdapter", doc_NoMetaStructureAdapter) - .def(init(python::arg("adapter"), - doc_NoMetaStructureAdapter_init)) - .def_pickle(StructureProxyPickleSuite()) + nb::class_ nometastructureadapter(m, + "NoMetaStructureAdapter", doc_NoMetaStructureAdapter); + nometastructureadapter + .def(nb::init(), nb::arg("adapter"), + doc_NoMetaStructureAdapter_init) ; + StructureProxyPickleSuite::bind(nometastructureadapter); typedef std::shared_ptr NoSymmetryStructureAdapterPtr; - class_, NoSymmetryStructureAdapterPtr>( - "NoSymmetryStructureAdapter", doc_NoSymmetryStructureAdapter) - .def(init(python::arg("adapter"), - doc_NoSymmetryStructureAdapter_init)) - .def_pickle(StructureProxyPickleSuite()) + nb::class_ nosymmetrystructureadapter(m, + "NoSymmetryStructureAdapter", doc_NoSymmetryStructureAdapter); + nosymmetrystructureadapter + .def(nb::init(), nb::arg("adapter"), + doc_NoSymmetryStructureAdapter_init) ; + StructureProxyPickleSuite::bind(nosymmetrystructureadapter); - def("nometa", nometa, doc_nometa); - def("nosymmetry", nosymmetry, doc_nosymmetry); - def("_emptyStructureAdapter", emptyStructureAdapter, + m.def("nometa", nometa, doc_nometa); + m.def("nosymmetry", nosymmetry, doc_nosymmetry); + m.def("_emptyStructureAdapter", emptyStructureAdapter, doc__emptyStructureAdapter); } diff --git a/src/extensions/wrap_StructureDifference.cpp b/src/extensions/wrap_StructureDifference.cpp index 0515cb6e..b141f736 100644 --- a/src/extensions/wrap_StructureDifference.cpp +++ b/src/extensions/wrap_StructureDifference.cpp @@ -17,8 +17,7 @@ * *****************************************************************************/ -#include -#include +#include #include @@ -27,16 +26,16 @@ #include "srreal_converters.hpp" +namespace nb = nanobind; + namespace srrealmodule { // declarations -void sync_StructureDifference(boost::python::object obj); +void sync_StructureDifference(nb::object obj); namespace nswrap_StructureDifference { -using namespace boost; using namespace diffpy::srreal; -using boost::python::slice; // docstrings ---------------------------------------------------------------- @@ -72,45 +71,45 @@ from stru0 atoms at indices pop0 and addition of add1 atoms in stru1.\n\ // wrappers ------------------------------------------------------------------ -python::list get_pop0(python::object obj) +nb::list get_pop0(nb::object obj) { - python::object pypop0 = obj.attr("_pop0"); + nb::object pypop0 = obj.attr("_pop0"); if (pypop0.is_none()) { const StructureDifference& sd = - python::extract(obj); + nb::cast(obj); pypop0 = obj.attr("_pop0") = convertToPythonList(sd.pop0); } - return python::extract(pypop0); + return nb::borrow(pypop0); } -void set_pop0(python::object obj, python::object value) +void set_pop0(nb::object obj, nb::object value) { - StructureDifference& sd = python::extract(obj); + StructureDifference& sd = nb::cast(obj); sd.pop0 = extractintvector(value); - get_pop0(obj)[slice()] = convertToPythonList(sd.pop0); + nb::cast(get_pop0(obj))[nb::slice(nb::none(), nb::none(), nb::none())] = convertToPythonList(sd.pop0); } -python::list get_add1(python::object obj) +nb::list get_add1(nb::object obj) { - python::object pyadd1 = obj.attr("_add1"); + nb::object pyadd1 = obj.attr("_add1"); if (pyadd1.is_none()) { const StructureDifference& sd = - python::extract(obj); + nb::cast(obj); pyadd1 = obj.attr("_add1") = convertToPythonList(sd.add1); } - return python::extract(pyadd1); + return nb::borrow(pyadd1); } -void set_add1(python::object obj, python::object value) +void set_add1(nb::object obj, nb::object value) { - StructureDifference& sd = python::extract(obj); + StructureDifference& sd = nb::cast(obj); sd.add1 = extractintvector(value); - get_add1(obj)[slice()] = convertToPythonList(sd.add1); + nb::cast(get_pop0(obj))[nb::slice(nb::none(), nb::none(), nb::none())] = convertToPythonList(sd.add1); } @@ -127,15 +126,14 @@ std::string get_diffmethod(const StructureDifference& sd) } const char* emsg = "Unknown internal value of StructureDifference::Method."; PyErr_SetString(PyExc_NotImplementedError, emsg); - boost::python::throw_error_already_set(); - abort(); + throw nb::python_error(); } -bool sd_allowsfastupdate(python::object obj) +bool sd_allowsfastupdate(nb::object obj) { sync_StructureDifference(obj); - StructureDifference& sd = python::extract(obj); + StructureDifference& sd = nb::cast(obj); return sd.allowsfastupdate(); } @@ -144,38 +142,34 @@ bool sd_allowsfastupdate(python::object obj) // this is a helper function to be called for Python-overridden // StructureAdapter::diff method -void sync_StructureDifference(boost::python::object obj) +void sync_StructureDifference(nb::object obj) { - using namespace boost::python; using diffpy::srreal::StructureDifference; - StructureDifference& sd = extract(obj); - object pypop0 = obj.attr("_pop0"); + StructureDifference& sd = nb::cast(obj); + nb::object pypop0 = obj.attr("_pop0"); if (!pypop0.is_none()) sd.pop0 = extractintvector(pypop0); - object pyadd1 = obj.attr("_add1"); + nb::object pyadd1 = obj.attr("_add1"); if (!pyadd1.is_none()) sd.add1 = extractintvector(pyadd1); } // Wrapper definitions ------------------------------------------------------- -void wrap_StructureDifference() +void wrap_StructureDifference(nb::module_& m) { using namespace nswrap_StructureDifference; - using namespace boost::python; - namespace bp = boost::python; - - class_("StructureDifference", doc_StructureDifference) - .def(init(bp::arg("sd"), - doc_StructureDifference_init_copy)) - .def(init( - (bp::arg("stru0"), bp::arg("stru1")), - doc_StructureDifference_init_structures)) - .def_readwrite("stru0", &StructureDifference::stru0) - .def_readwrite("stru1", &StructureDifference::stru1) - .add_property("pop0", get_pop0, set_pop0) - .setattr("_pop0", bp::object()) - .add_property("add1", get_add1, set_add1) - .setattr("_add1", bp::object()) - .add_property("diffmethod", + + nb::class_ sd(m, "StructureDifference", doc_StructureDifference); + sd + .def(nb::init(), nb::arg("sd"), + doc_StructureDifference_init_copy) + .def(nb::init(), + nb::arg("stru0"), nb::arg("stru1"), + doc_StructureDifference_init_structures) + .def_rw("stru0", &StructureDifference::stru0) + .def_rw("stru1", &StructureDifference::stru1) + .def_prop_rw("pop0", get_pop0, set_pop0) + .def_prop_rw("add1", get_add1, set_add1) + .def_prop_ro("diffmethod", get_diffmethod, doc_StructureDifference_diffmethod) .def("allowsfastupdate", @@ -183,6 +177,8 @@ void wrap_StructureDifference() doc_StructureDifference_allowsfastupdate) ; + sd.attr("_pop0") = nb::none(); + sd.attr("_add1") = nb::none(); } } // namespace srrealmodule From c97ea6df5745322377732fd5c1201a270fe2e6cc Mon Sep 17 00:00:00 2001 From: symscae Date: Thu, 25 Jun 2026 15:55:07 -0700 Subject: [PATCH 13/18] fix build --- src/extensions/srreal_converters.hpp | 3 ++- src/extensions/srreal_ext.cpp | 1 + src/extensions/srreal_pickling.hpp | 17 ++++++++++++++--- src/extensions/wrap_ObjCrystAdapters.cpp | 5 ++--- src/extensions/wrap_PairQuantity.cpp | 5 +++-- src/extensions/wrap_StructureAdapter.cpp | 6 ++---- 6 files changed, 24 insertions(+), 13 deletions(-) diff --git a/src/extensions/srreal_converters.hpp b/src/extensions/srreal_converters.hpp index 21018a4a..9f0ae25f 100644 --- a/src/extensions/srreal_converters.hpp +++ b/src/extensions/srreal_converters.hpp @@ -24,6 +24,7 @@ #define SRREAL_CONVERTERS_HPP_INCLUDED #include +#include #include #include @@ -373,7 +374,7 @@ convertToPythonDict(const T& value) { nb::dict rv; for (auto const& item : value) - rv[item.first] = item.second; + rv[nb::cast(item.first)] = nb::cast(item.second); return rv; } diff --git a/src/extensions/srreal_ext.cpp b/src/extensions/srreal_ext.cpp index 6a932aa1..09e33862 100644 --- a/src/extensions/srreal_ext.cpp +++ b/src/extensions/srreal_ext.cpp @@ -18,6 +18,7 @@ #include +#include "srreal_numpy_symbol.hpp" #include // Declaration of the external wrappers -------------------------------------- diff --git a/src/extensions/srreal_pickling.hpp b/src/extensions/srreal_pickling.hpp index 3ce3bf88..babd791c 100644 --- a/src/extensions/srreal_pickling.hpp +++ b/src/extensions/srreal_pickling.hpp @@ -23,6 +23,7 @@ #include #include +#include #include #include @@ -82,14 +83,24 @@ nb::object resolve_state_object(nb::object value) template void assign_state_object(T target, nb::object value) { - if (!value.is_none()) target = nb::cast(value); + if (!value.is_none()) target = value; } template void construct_for_unpickle(T* tobj) { - new (tobj) T(); + if constexpr (std::is_default_constructible_v && !std::is_abstract_v) + { + new (tobj) T(); + } + else + { + throw nb::type_error( + "cannot unpickle an uninitialized non-default-constructible " + "C++ instance" + ); + } } @@ -127,7 +138,7 @@ class SerializationPickleSuite static nb::tuple getstate(nb::object obj) { const T& tobj = nb::cast(obj); - nb::bytes content = serialization_tobytes(*tobj); + nb::bytes content = serialization_tobytes(tobj); if constexpr (pickledict) { diff --git a/src/extensions/wrap_ObjCrystAdapters.cpp b/src/extensions/wrap_ObjCrystAdapters.cpp index 250d12a8..1f8d8bb9 100644 --- a/src/extensions/wrap_ObjCrystAdapters.cpp +++ b/src/extensions/wrap_ObjCrystAdapters.cpp @@ -33,7 +33,6 @@ namespace nb = nanobind; namespace srrealmodule { namespace nswrap_ObjCrystAdapters { -using namespace boost; using namespace diffpy::srreal; // docstrings ---------------------------------------------------------------- @@ -81,12 +80,12 @@ StructureAdapterPtr convertObjCrystCrystal(const Crystal& mol) #else -StructureAdapterPtr convertObjCrystMolecule(python::object mol) +StructureAdapterPtr convertObjCrystMolecule(nb::object) { throw nb::type_error("ObjCryst support not available."); } -StructureAdapterPtr convertObjCrystCrystal(python::object cryst) +StructureAdapterPtr convertObjCrystCrystal(nb::object cryst) { // raise the same exception as for the Molecule return convertObjCrystMolecule(cryst); diff --git a/src/extensions/wrap_PairQuantity.cpp b/src/extensions/wrap_PairQuantity.cpp index f467feb4..cb26522b 100644 --- a/src/extensions/wrap_PairQuantity.cpp +++ b/src/extensions/wrap_PairQuantity.cpp @@ -334,12 +334,13 @@ inline nb::bytes string_to_pybytes(const std::string &s) // representation of QuantityType objects nb::object repr_QuantityType(const QuantityType& v) { - nb::tuple t = nb::make_tuple(v.size()); + nb::list values; for (size_t i = 0; i < v.size(); ++i) { - t[i] = nb::cast(v[i]); + values.append(v[i]); } + nb::object t = nb::module_::import_("builtins").attr("tuple")(values); return nb::str("QuantityType{}").attr("format")(nb::repr(t)); } diff --git a/src/extensions/wrap_StructureAdapter.cpp b/src/extensions/wrap_StructureAdapter.cpp index d3a63811..8fd5cfda 100644 --- a/src/extensions/wrap_StructureAdapter.cpp +++ b/src/extensions/wrap_StructureAdapter.cpp @@ -460,7 +460,7 @@ class StructureProxyPickleSuite static void bind(C& cls) { cls - .def("__reduce__", [](const T& self) + .def("__reduce__", [](T& self) { StructureAdapterPtr src = self.getSourceStructure(); @@ -503,8 +503,6 @@ void wrap_StructureAdapter(nb::module_& m) "StructureAdapter", doc_StructureAdapter); structureadapter .def(nb::init<>()) - .def("__init__", StructureAdapter_constructor, - doc_StructureAdapter___init__fromstring) .def("clone", &StructureAdapter::clone, doc_StructureAdapter_clone) @@ -545,7 +543,7 @@ void wrap_StructureAdapter(nb::module_& m) nb::arg("other"), doc_StructureAdapter_diff) ; - StructureProxyPickleSuite::bind(structureadapter); + StructureAdapterPickleSuite::bind(structureadapter); typedef std::shared_ptr NoMetaStructureAdapterPtr; From 31869d3fcf925ffff5f91d52669a971813b97838 Mon Sep 17 00:00:00 2001 From: symscae Date: Thu, 25 Jun 2026 19:05:06 -0700 Subject: [PATCH 14/18] restore default constructors --- src/extensions/wrap_AtomRadiiTable.cpp | 1 + src/extensions/wrap_BVParametersTable.cpp | 1 + src/extensions/wrap_BVSCalculator.cpp | 1 + src/extensions/wrap_EventTicker.cpp | 1 + src/extensions/wrap_OverlapCalculator.cpp | 1 + src/extensions/wrap_PairQuantity.cpp | 2 ++ src/extensions/wrap_PeakWidthModel.cpp | 1 + src/extensions/wrap_ScatteringFactorTable.cpp | 1 + src/extensions/wrap_StructureDifference.cpp | 1 + 9 files changed, 10 insertions(+) diff --git a/src/extensions/wrap_AtomRadiiTable.cpp b/src/extensions/wrap_AtomRadiiTable.cpp index 80dd1c8f..b747941d 100644 --- a/src/extensions/wrap_AtomRadiiTable.cpp +++ b/src/extensions/wrap_AtomRadiiTable.cpp @@ -275,6 +275,7 @@ void wrap_AtomRadiiTable(nb::module_& m) constantradiitable(m, "ConstantRadiiTable", doc_ConstantRadiiTable); // docstring updates constantradiitable + .def(nb::init<>()) .def("create", &ConstantRadiiTable::create, doc_ConstantRadiiTable_create) .def("clone", &ConstantRadiiTable::clone, diff --git a/src/extensions/wrap_BVParametersTable.cpp b/src/extensions/wrap_BVParametersTable.cpp index b35dba90..3bfd24f3 100644 --- a/src/extensions/wrap_BVParametersTable.cpp +++ b/src/extensions/wrap_BVParametersTable.cpp @@ -269,6 +269,7 @@ void wrap_BVParametersTable(nb::module_& m) nb::class_ bvparam(m, "BVParam", doc_BVParam); bvparam + .def(nb::init<>()) .def(nb::init(), diff --git a/src/extensions/wrap_BVSCalculator.cpp b/src/extensions/wrap_BVSCalculator.cpp index c39b6328..151f8957 100644 --- a/src/extensions/wrap_BVSCalculator.cpp +++ b/src/extensions/wrap_BVSCalculator.cpp @@ -89,6 +89,7 @@ void wrap_BVSCalculator(nb::module_& m) nb::class_ bvscalculator(m, "BVSCalculator", doc_BVSCalculator); bvscalculator + .def(nb::init<>()) .def_prop_ro("value", value_asarray, doc_BVSCalculator_value) .def_prop_ro("valences", valences_asarray, diff --git a/src/extensions/wrap_EventTicker.cpp b/src/extensions/wrap_EventTicker.cpp index 84d697dd..4c3b89aa 100644 --- a/src/extensions/wrap_EventTicker.cpp +++ b/src/extensions/wrap_EventTicker.cpp @@ -93,6 +93,7 @@ void wrap_EventTicker(nb::module_& m) nb::class_ eventticker(m, "EventTicker", doc_EventTicker); eventticker + .def(nb::init<>()) .def(nb::init(), doc_EventTicker_cp) .def("__repr__", repr_EventTicker, doc_EventTicker___repr__) .def(nb::self < nb::self) diff --git a/src/extensions/wrap_OverlapCalculator.cpp b/src/extensions/wrap_OverlapCalculator.cpp index 765cf162..fadd472e 100644 --- a/src/extensions/wrap_OverlapCalculator.cpp +++ b/src/extensions/wrap_OverlapCalculator.cpp @@ -234,6 +234,7 @@ void wrap_OverlapCalculator(nb::module_& m) nb::class_ overlapcalculator(m, "OverlapCalculator", doc_OverlapCalculator); overlapcalculator + .def(nb::init<>()) .def_prop_ro("overlaps", overlaps_asarray, doc_OverlapCalculator_overlaps) diff --git a/src/extensions/wrap_PairQuantity.cpp b/src/extensions/wrap_PairQuantity.cpp index cb26522b..8ba38a89 100644 --- a/src/extensions/wrap_PairQuantity.cpp +++ b/src/extensions/wrap_PairQuantity.cpp @@ -726,6 +726,7 @@ void wrap_PairQuantity(nb::module_& m) nb::class_ basepq(m, "BasePairQuantity"); basepq + .def(nb::init<>()) .def("eval", eval_asarray, nb::arg("stru")=None, doc_BasePairQuantity_eval) .def_prop_ro("value", value_asarray, @@ -780,6 +781,7 @@ void wrap_PairQuantity(nb::module_& m) nb::class_ pq(m, "PairQuantity", doc_PairQuantity); pq + .def(nb::init<>()) .def("ticker", &PairQuantityExposed::ticker, nb::rv_policy::reference_internal, diff --git a/src/extensions/wrap_PeakWidthModel.cpp b/src/extensions/wrap_PeakWidthModel.cpp index 69924f31..fa1968f8 100644 --- a/src/extensions/wrap_PeakWidthModel.cpp +++ b/src/extensions/wrap_PeakWidthModel.cpp @@ -285,6 +285,7 @@ void wrap_PeakWidthModel(nb::module_& m) SerializationPickleSuite::bind(jeongpeakwidth); nb::class_(m, "PeakWidthModelOwner", doc_PeakWidthModelOwner) + .def(nb::init<>()) .def_prop_rw("peakwidthmodel", getpwmodel, setpwmodel, diff --git a/src/extensions/wrap_ScatteringFactorTable.cpp b/src/extensions/wrap_ScatteringFactorTable.cpp index db9e2ba9..d0036581 100644 --- a/src/extensions/wrap_ScatteringFactorTable.cpp +++ b/src/extensions/wrap_ScatteringFactorTable.cpp @@ -445,6 +445,7 @@ void wrap_ScatteringFactorTable(nb::module_& m) nb::class_(m, "ScatteringFactorTableOwner", doc_ScatteringFactorTableOwner) + .def(nb::init<>()) .def_prop_rw("scatteringfactortable", getsftable, setsftable, diff --git a/src/extensions/wrap_StructureDifference.cpp b/src/extensions/wrap_StructureDifference.cpp index b141f736..0f05ecd9 100644 --- a/src/extensions/wrap_StructureDifference.cpp +++ b/src/extensions/wrap_StructureDifference.cpp @@ -160,6 +160,7 @@ void wrap_StructureDifference(nb::module_& m) nb::class_ sd(m, "StructureDifference", doc_StructureDifference); sd + .def(nb::init<>()) .def(nb::init(), nb::arg("sd"), doc_StructureDifference_init_copy) .def(nb::init(), From 0cd37e2475ee3d8f782ccdd86221b25c4a14d649 Mon Sep 17 00:00:00 2001 From: symscae Date: Thu, 25 Jun 2026 19:47:48 -0700 Subject: [PATCH 15/18] corrected code behaviors --- src/extensions/srreal_converters.hpp | 1 + src/extensions/srreal_pickling.cpp | 1 - src/extensions/srreal_pickling.hpp | 1 + src/extensions/srreal_registry.hpp | 1 + src/extensions/wrap_Attributes.cpp | 1 - src/extensions/wrap_PDFEnvelope.cpp | 4 ++-- src/extensions/wrap_PairQuantity.cpp | 8 ++++---- src/extensions/wrap_PeakWidthModel.cpp | 1 + src/extensions/wrap_StructureDifference.cpp | 5 +++-- 9 files changed, 13 insertions(+), 10 deletions(-) diff --git a/src/extensions/srreal_converters.hpp b/src/extensions/srreal_converters.hpp index 9f0ae25f..2a85e533 100644 --- a/src/extensions/srreal_converters.hpp +++ b/src/extensions/srreal_converters.hpp @@ -25,6 +25,7 @@ #include #include +#include #include #include diff --git a/src/extensions/srreal_pickling.cpp b/src/extensions/srreal_pickling.cpp index 8514e969..fe971ddb 100644 --- a/src/extensions/srreal_pickling.cpp +++ b/src/extensions/srreal_pickling.cpp @@ -17,7 +17,6 @@ *****************************************************************************/ #include "srreal_pickling.hpp" -#include #include namespace srrealmodule { diff --git a/src/extensions/srreal_pickling.hpp b/src/extensions/srreal_pickling.hpp index babd791c..8263f8ad 100644 --- a/src/extensions/srreal_pickling.hpp +++ b/src/extensions/srreal_pickling.hpp @@ -20,6 +20,7 @@ #define SRREAL_PICKLING_HPP_INCLUDED #include +#include #include #include diff --git a/src/extensions/srreal_registry.hpp b/src/extensions/srreal_registry.hpp index c08bfc73..c3487144 100644 --- a/src/extensions/srreal_registry.hpp +++ b/src/extensions/srreal_registry.hpp @@ -20,6 +20,7 @@ #define SRREAL_REGISTRY_HPP_INCLUDED #include +#include namespace nb = nanobind; diff --git a/src/extensions/wrap_Attributes.cpp b/src/extensions/wrap_Attributes.cpp index dd68bcc5..fa3cb1ee 100644 --- a/src/extensions/wrap_Attributes.cpp +++ b/src/extensions/wrap_Attributes.cpp @@ -177,7 +177,6 @@ void registerPythonDoubleAttribute(nb::object owner, void wrap_Attributes(nb::module_& m) { using namespace nswrap_Attributes; - const nb::object None; // ready for class definition nb::class_(m, "Attributes", nb::dynamic_attr(), doc_Attributes) .def(nb::init<>()) diff --git a/src/extensions/wrap_PDFEnvelope.cpp b/src/extensions/wrap_PDFEnvelope.cpp index 5e224540..dd5595bc 100644 --- a/src/extensions/wrap_PDFEnvelope.cpp +++ b/src/extensions/wrap_PDFEnvelope.cpp @@ -232,10 +232,10 @@ void wrap_PDFEnvelope(nb::module_ &m) SerializationPickleSuite::bind(sphshapeenvelope); nb::class_ stepcutenvelope(m, "StepCutEnvelope", doc_StepCutEnvelope); - sphshapeenvelope + stepcutenvelope .def(nb::init<>()) ; - SerializationPickleSuite::bind(sphshapeenvelope); + SerializationPickleSuite::bind(stepcutenvelope); } diff --git a/src/extensions/wrap_PairQuantity.cpp b/src/extensions/wrap_PairQuantity.cpp index 8ba38a89..45c33f55 100644 --- a/src/extensions/wrap_PairQuantity.cpp +++ b/src/extensions/wrap_PairQuantity.cpp @@ -32,6 +32,7 @@ #include #include #include +#include #include @@ -716,7 +717,6 @@ void wrap_PairQuantity(nb::module_& m) { using namespace nswrap_PairQuantity; using diffpy::Attributes; - const nb::object None; typedef StructureAdapterPtr&(PairQuantity::*getstru)(); @@ -727,7 +727,7 @@ void wrap_PairQuantity(nb::module_& m) basepq(m, "BasePairQuantity"); basepq .def(nb::init<>()) - .def("eval", eval_asarray, nb::arg("stru")=None, + .def("eval", eval_asarray, nb::arg("stru")=nb::none(), doc_BasePairQuantity_eval) .def_prop_ro("value", value_asarray, doc_BasePairQuantity_value) @@ -760,14 +760,14 @@ void wrap_PairQuantity(nb::module_& m) doc_BasePairQuantity_invertMask) .def("setPairMask", set_pair_mask, nb::arg("i"), nb::arg("j"), nb::arg("mask"), - nb::arg("others")=None, + nb::arg("others")=nb::none(), doc_BasePairQuantity_setPairMask) .def("getPairMask", &PairQuantity::getPairMask, nb::arg("i"), nb::arg("j"), doc_BasePairQuantity_getPairMask) .def("setTypeMask", set_type_mask, nb::arg("tpi"), nb::arg("tpj"), nb::arg("mask"), - nb::arg("others")=None, + nb::arg("others")=nb::none(), doc_BasePairQuantity_setTypeMask) .def("getTypeMask", &PairQuantity::getTypeMask, nb::arg("tpi"), nb::arg("tpj"), diff --git a/src/extensions/wrap_PeakWidthModel.cpp b/src/extensions/wrap_PeakWidthModel.cpp index fa1968f8..fb8cf6fd 100644 --- a/src/extensions/wrap_PeakWidthModel.cpp +++ b/src/extensions/wrap_PeakWidthModel.cpp @@ -26,6 +26,7 @@ #include #include #include +#include #include #include "srreal_converters.hpp" diff --git a/src/extensions/wrap_StructureDifference.cpp b/src/extensions/wrap_StructureDifference.cpp index 0f05ecd9..b586b80d 100644 --- a/src/extensions/wrap_StructureDifference.cpp +++ b/src/extensions/wrap_StructureDifference.cpp @@ -109,7 +109,7 @@ void set_add1(nb::object obj, nb::object value) { StructureDifference& sd = nb::cast(obj); sd.add1 = extractintvector(value); - nb::cast(get_pop0(obj))[nb::slice(nb::none(), nb::none(), nb::none())] = convertToPythonList(sd.add1); + nb::cast(get_add1(obj))[nb::slice(nb::none(), nb::none(), nb::none())] = convertToPythonList(sd.add1); } @@ -158,7 +158,8 @@ void wrap_StructureDifference(nb::module_& m) { using namespace nswrap_StructureDifference; - nb::class_ sd(m, "StructureDifference", doc_StructureDifference); + nb::class_ sd(m, "StructureDifference", + doc_StructureDifference, nb::dynamic_attr()); sd .def(nb::init<>()) .def(nb::init(), nb::arg("sd"), From 1627235dc64270c7c8c90c66005b29c1cbf1c1a4 Mon Sep 17 00:00:00 2001 From: symscae Date: Thu, 25 Jun 2026 20:39:12 -0700 Subject: [PATCH 16/18] restore PDFCalculator behavior --- src/extensions/wrap_PDFCalculators.cpp | 94 ++++++++++++++++++++++++++ 1 file changed, 94 insertions(+) diff --git a/src/extensions/wrap_PDFCalculators.cpp b/src/extensions/wrap_PDFCalculators.cpp index 2393b6ee..71801e4d 100644 --- a/src/extensions/wrap_PDFCalculators.cpp +++ b/src/extensions/wrap_PDFCalculators.cpp @@ -163,6 +163,35 @@ Return a tuple of (f, qstep). These can be used with the complementary\n\ fftftog function to recover the original signal g.\n\ "; +const char* doc_PeakWidthModelOwner_peakwidthmodel = "\ +PeakWidthModel object used for calculating the FWHM of the PDF peaks.\n\ +This attribute can be assigned either a PeakWidthModel-derived object\n\ +or a string name of a registered PeakWidthModel class.\n\ +Use PeakWidthModel.getRegisteredTypes() for a set of registered names.\n\ +"; + +const char* doc_ScatteringFactorTableOwner_scatteringfactortable = "\ +ScatteringFactorTable object used for a lookup of scattering factors.\n\ +This can be also set with the setScatteringFactorTableByType method.\n\ +"; + +const char* doc_ScatteringFactorTableOwner_setScatteringFactorTableByType = "\ +Set internal ScatteringFactorTable according to specified string type.\n\ +\n\ +tp -- string identifier of a registered ScatteringFactorTable type.\n\ + Use ScatteringFactorTable.getRegisteredTypes for the allowed values.\n\ +\n\ +Deprecated: This method is deprecated and will be removed in the 2.0.0 release.\n\ +Use direct assignment to the `scatteringfactortable` property instead, for example:\n\ + obj.scatteringfactortable = SFTNeutron()\n\ +No return value.\n\ +"; + +const char* doc_ScatteringFactorTableOwner_getRadiationType = "\ +Return string identifying the radiation type.\n\ +'X' for x-rays, 'N' for neutrons.\n\ +"; + // wrappers ------------------------------------------------------------------ DECLARE_PYARRAY_METHOD_WRAPPER(getPDF, getPDF_asarray) @@ -266,7 +295,55 @@ PDFEnvelopePtr getoneenvelope(T& obj, const std::string& tp) } +// wrappers for PeakWidthModelOwner behavior + +template +PeakWidthModelPtr getpeakwidthmodel(T& obj) +{ + return obj.getPeakWidthModel(); +} + +DECLARE_BYTYPE_SETTER_WRAPPER(setPeakWidthModel, setpeakwidthmodel) + +// wrappers for ScatteringFactorTableOwner behavior + +template +ScatteringFactorTablePtr getscatteringfactortable(T& obj) +{ + return obj.getScatteringFactorTable(); +} + +DECLARE_BYTYPE_SETTER_WRAPPER(setScatteringFactorTable, setscatteringfactortable) + +template +void setscatteringfactortablebytype(T& obj, const std::string& tp) +{ + try + { + nb::object warnings = nb::module_::import_("warnings"); + nb::object builtins = nb::module_::import_("builtins"); + nb::object DeprecationWarning = builtins.attr("DeprecationWarning"); + warnings.attr("warn")( + std::string("setScatteringFactorTableByType is deprecated; " + "assign the 'scatteringfactortable' property directly, for example:\n" + "obj.scatteringfactortable = SFTNeutron()"), + DeprecationWarning, + 2); + } + catch (...) { /* don't let warnings break the binding */ } + obj.setScatteringFactorTableByType(tp); +} + +template +std::string getradiationtype(T& obj) +{ + return obj.getRadiationType(); +} + + // wrap shared methods and attributes of PDFCalculators +// TODO: since nanobind doesn't allow multiple inheritance, +// we may need to introduce a header to better address this. template C& wrap_PDFCommon(C& cls) @@ -293,6 +370,23 @@ C& wrap_PDFCommon(C& cls) nb::arg("tp"), doc_PDFCommon_getEnvelope) .def("clearEnvelopes", &W::clearEnvelopes, doc_PDFCommon_clearEnvelopes) + // PeakWidthModelOwner functions + .def_prop_rw("peakwidthmodel", + getpeakwidthmodel, + setpeakwidthmodel, + doc_PeakWidthModelOwner_peakwidthmodel) + // ScatteringFactorTableOwner behavior + .def_prop_rw("scatteringfactortable", + getscatteringfactortable, + setscatteringfactortable, + doc_ScatteringFactorTableOwner_scatteringfactortable) + .def("setScatteringFactorTableByType", + setscatteringfactortablebytype, + nb::arg("tp"), + doc_ScatteringFactorTableOwner_setScatteringFactorTableByType) + .def("getRadiationType", + getradiationtype, + doc_ScatteringFactorTableOwner_getRadiationType) ; return cls; } From 063436eb32cefbeea62922b9f99bc8784b54285b Mon Sep 17 00:00:00 2001 From: symscae Date: Fri, 26 Jun 2026 10:15:12 -0700 Subject: [PATCH 17/18] reworked pickling system --- src/extensions/srreal_pickling.hpp | 396 ++++++++++++++---- src/extensions/srreal_registry.hpp | 1 + src/extensions/wrap_AtomRadiiTable.cpp | 8 +- .../wrap_AtomicStructureAdapter.cpp | 16 +- src/extensions/wrap_OverlapCalculator.cpp | 35 +- src/extensions/wrap_PDFBaseline.cpp | 8 +- src/extensions/wrap_PDFCalculators.cpp | 145 +++++-- src/extensions/wrap_PDFEnvelope.cpp | 16 +- src/extensions/wrap_PairQuantity.cpp | 64 ++- src/extensions/wrap_PeakProfile.cpp | 8 +- src/extensions/wrap_PeakWidthModel.cpp | 8 +- src/extensions/wrap_ScatteringFactorTable.cpp | 8 +- src/extensions/wrap_StructureAdapter.cpp | 76 +++- 13 files changed, 639 insertions(+), 150 deletions(-) diff --git a/src/extensions/srreal_pickling.hpp b/src/extensions/srreal_pickling.hpp index 8263f8ad..495cd338 100644 --- a/src/extensions/srreal_pickling.hpp +++ b/src/extensions/srreal_pickling.hpp @@ -21,10 +21,13 @@ #include #include +#include #include #include -#include +#include +#include +#include #include #include @@ -33,10 +36,16 @@ namespace nb = nanobind; namespace srrealmodule { +struct PythonTrampolineTag +{ + virtual ~PythonTrampolineTag() = default; +}; + + inline void ensure_tuple_length(nb::tuple state, const int statelen) { - if (state.size() == statelen) return; + if (state.size() == static_cast(statelen)) return; PyErr_Format( PyExc_ValueError, "expected %d-item tuple in call to __setstate__; got %zd", @@ -54,75 +63,222 @@ nb::bytes serialization_tobytes(const T& tobj) return nb::bytes(s.data(), s.size()); } -template -std::string bytes_to_string(const H& h) +inline +std::string bytes_to_string(nb::handle h) { - nb::bytes py_content(h); + if (!PyBytes_Check(h.ptr())) + { + PyErr_SetString(PyExc_TypeError, "expected bytes"); + nb::raise_python_error(); + } + char *buffer = nullptr; + Py_ssize_t size = 0; + if (PyBytes_AsStringAndSize(h.ptr(), &buffer, &size) < 0) + nb::raise_python_error(); return std::string( - static_cast(py_content.data()), - py_content.size() + static_cast(buffer), + static_cast(size) ); } -template -bool is_wrapper(nb::object& obj) +inline +nb::object runtime_type(nb::handle obj) { - T* p = nullptr; - return nb::try_cast(obj, p, false) && p; + return nb::borrow(reinterpret_cast(Py_TYPE(obj.ptr()))); } -template -nb::object resolve_state_object(nb::object value) +inline +nb::object get_instance_dict(nb::handle obj) { - // return None if value holds a pristine C++ instance of T - return is_wrapper(value) ? value : nb::none(); + PyObject *dict = PyObject_GetAttrString(obj.ptr(), "__dict__"); + if (!dict) + { + if (PyErr_ExceptionMatches(PyExc_AttributeError)) + { + PyErr_Clear(); + return nb::none(); + } + nb::raise_python_error(); + } + return nb::steal(dict); } -template -void assign_state_object(T target, nb::object value) +inline +bool state_manages_dict(nb::handle obj, bool default_policy) { - if (!value.is_none()) target = value; + PyObject *flag = + PyObject_GetAttrString(reinterpret_cast(Py_TYPE(obj.ptr())), + "__getstate_manages_dict__"); + if (!flag) + { + if (PyErr_ExceptionMatches(PyExc_AttributeError)) + { + PyErr_Clear(); + return default_policy; + } + nb::raise_python_error(); + } + nb::object flag_obj = nb::steal(flag); + if (flag_obj.is_none()) return false; + int rv = PyObject_IsTrue(flag_obj.ptr()); + if (rv < 0) nb::raise_python_error(); + return rv != 0; } -template -void construct_for_unpickle(T* tobj) +inline +void ensure_dict_is_managed_or_empty(nb::handle obj, bool manages_dict) { - if constexpr (std::is_default_constructible_v && !std::is_abstract_v) + if (manages_dict) return; + nb::object dict_obj = get_instance_dict(obj); + if (dict_obj.is_none()) return; + Py_ssize_t n = PyMapping_Size(dict_obj.ptr()); + if (n < 0) nb::raise_python_error(); + if (n == 0) return; + throw std::runtime_error( + "Incomplete pickle support (__getstate_manages_dict__ not set)" + ); +} + + +inline +void ensure_mapping_state(nb::handle state) +{ + if (!PyMapping_Check(state.ptr())) { - new (tobj) T(); + PyErr_SetString(PyExc_TypeError, "pickle __dict__ state must be a mapping"); + nb::raise_python_error(); } - else + + PyObject *keys = PyMapping_Keys(state.ptr()); + if (!keys) { - throw nb::type_error( - "cannot unpickle an uninitialized non-default-constructible " - "C++ instance" - ); + if (PyErr_ExceptionMatches(PyExc_AttributeError)) + { + PyErr_Clear(); + PyErr_SetString( + PyExc_TypeError, + "pickle __dict__ state must be a mapping" + ); + } + nb::raise_python_error(); } + Py_DECREF(keys); } -template -T& ensure_instance_ready(nb::handle obj) +inline +void restore_instance_dict(nb::handle obj, nb::handle dict_state) { - T* tobj = nb::inst_ptr(obj); + if (dict_state.is_none()) return; + ensure_mapping_state(dict_state); - if (!nb::inst_ready(obj)) + nb::object dict_obj = get_instance_dict(obj); + if (dict_obj.is_none()) + { + Py_ssize_t n = PyMapping_Size(dict_state.ptr()); + if (n < 0) nb::raise_python_error(); + if (n == 0) return; + PyErr_SetString(PyExc_TypeError, "object has no __dict__ to restore"); + nb::raise_python_error(); + } + if (!PyDict_Check(dict_obj.ptr())) { - construct_for_unpickle(tobj); - nb::inst_mark_ready(obj); + PyErr_SetString(PyExc_TypeError, "object __dict__ is not a dict"); + nb::raise_python_error(); } + if (PyDict_Update(dict_obj.ptr(), dict_state.ptr()) < 0) + nb::raise_python_error(); +} + - return *tobj; +template +bool is_python_trampoline(T *ptr) +{ + return dynamic_cast(ptr) != nullptr; } +template +bool is_python_trampoline(nb::handle obj) +{ + T *ptr = nullptr; + return nb::try_cast(obj, ptr, false) && + ptr && is_python_trampoline(ptr); +} + + +template +void assign_pointer_state(std::shared_ptr& slot, nb::handle value) +{ + nb::object value_obj = nb::borrow(value); + if (value_obj.is_none()) return; + slot = nb::cast>(value_obj); +} + + +template +nb::object resolve_state_pointer(const std::shared_ptr& value) +{ + // Return None for native extension objects. Python-defined wrappers are + // pickled independently so their dictionaries and virtual overrides survive. + if (!value || !std::dynamic_pointer_cast(value)) + return nb::none(); + return nb::cast(value); +} + + +template +class ScopedSharedPtrReplacement +{ + public: + + ScopedSharedPtrReplacement( + std::shared_ptr& slot, + std::shared_ptr replacement) : + mslot(slot), + moriginal(slot), + mstate(resolve_state_pointer(moriginal)), + mreplacement(std::move(replacement)) + { + if (mstate.is_none()) return; + mslot = mreplacement; + mactive = true; + } + + ~ScopedSharedPtrReplacement() + { + restore(); + } + + nb::object state_object() const + { + return mstate; + } + + void restore() noexcept + { + if (!mactive) return; + mslot = moriginal; + mactive = false; + } + + private: + + std::shared_ptr& mslot; + std::shared_ptr moriginal; + nb::object mstate; + std::shared_ptr mreplacement; + bool mactive = false; +}; + + enum {DICT_IGNORE=false, DICT_PICKLE=true}; -template +template class SerializationPickleSuite { public: @@ -133,7 +289,16 @@ class SerializationPickleSuite cls .def("__getstate__", getstate) .def("__setstate__", setstate) + .def("__reduce__", reduce) ; + if constexpr (pickledict) + { + cls.attr("__getstate_manages_dict__") = nb::bool_(true); + } + else + { + cls.attr("__getstate_manages_dict__") = nb::none(); + } } static nb::tuple getstate(nb::object obj) @@ -141,49 +306,56 @@ class SerializationPickleSuite const T& tobj = nb::cast(obj); nb::bytes content = serialization_tobytes(tobj); - if constexpr (pickledict) + bool manages_dict = state_manages_dict(obj, pickledict); + ensure_dict_is_managed_or_empty(obj, manages_dict); + if (manages_dict) { return nb::make_tuple( content, - nb::getattr(obj, "__dict__") + get_instance_dict(obj) ); - } - else - { - return nb::make_tuple(content); } + return nb::make_tuple(content); } static void setstate( nb::object obj, nb::tuple state) { - constexpr int statelen = pickledict ? 2 : 1; - ensure_tuple_length(state, statelen); + bool manages_dict = state_manages_dict(obj, pickledict); + ensure_tuple_length(state, manages_dict ? 2 : 1); // load the C++ object - T& tobj = ensure_instance_ready(obj); + T& tobj = nb::cast(obj); diffpy::serialization_fromstring(tobj, bytes_to_string(state[0])); // restore the object's __dict__ - if constexpr (pickledict) + if (manages_dict) { - nb::object dict_obj = nb::getattr(obj, "__dict__"); - nb::dict d = nb::borrow(dict_obj); - d.update(state[1]); + ensure_dict_is_managed_or_empty(obj, manages_dict); + restore_instance_dict(obj, state[1]); } } + static nb::tuple reduce(nb::object obj) + { + return nb::make_tuple( + runtime_type(obj), + nb::make_tuple(), + obj.attr("__getstate__")() + ); + } + static bool getstate_manages_dict() { return pickledict; } }; // class SerializationPickleSuite -template +template class PairQuantityPickleSuite : - public SerializationPickleSuite + public SerializationPickleSuite { private: - typedef SerializationPickleSuite Super; + typedef SerializationPickleSuite Super; public: @@ -193,21 +365,57 @@ class PairQuantityPickleSuite : cls .def("__getstate__", getstate) .def("__setstate__", setstate) + .def("__reduce__", reduce) ; + if constexpr (pickledict) + { + cls.attr("__getstate_manages_dict__") = nb::bool_(true); + } + else + { + cls.attr("__getstate_manages_dict__") = nb::none(); + } } static nb::tuple getstate(nb::object obj) { using namespace diffpy::srreal; - // store the original structure object - nb::object stru = obj.attr("getStructure")(); + T& pq = nb::cast(obj); // temporarily remove structure from the pair quantity - T& pq = ensure_instance_ready(obj); - StructureAdapterPtr pstru = - replacePairQuantityStructure(pq, StructureAdapterPtr()); - nb::object state0 = Super::getstate(obj); - // restore the original structure - replacePairQuantityStructure(pq, pstru); + StructureAdapterPtr& structure_slot = pq.getStructure(); + StructureAdapterPtr pstru = structure_slot; + nb::object stru = pstru ? nb::cast(pstru) : nb::none(); + structure_slot.reset(); + struct RestoreStructure + { + StructureAdapterPtr& slot; + StructureAdapterPtr pstru; + bool active = true; + + ~RestoreStructure() + { + restore(); + } + + void restore() noexcept + { + if (!active) return; + slot = pstru; + active = false; + } + } restore{structure_slot, pstru}; + + nb::object state0; + try + { + state0 = Super::getstate(obj); + } + catch (...) + { + restore.restore(); + throw; + } + restore.restore(); return nb::make_tuple(state0, stru); } @@ -222,14 +430,19 @@ class PairQuantityPickleSuite : Super::setstate(obj, state0); // restore the structure object StructureAdapterPtr pstru = nb::cast(state[1]); - T& pq = ensure_instance_ready(obj); - replacePairQuantityStructure(pq, pstru); + T& pq = nb::cast(obj); + pq.getStructure() = pstru; + } + + static nb::tuple reduce(nb::object obj) + { + return Super::reduce(obj); } }; // class PairQuantityPickleSuite -template +template class StructureAdapterPickleSuite { public: @@ -240,15 +453,23 @@ class StructureAdapterPickleSuite cls .def("__getstate__", getstate) .def("__setstate__", setstate) + .def("__reduce__", reduce) ; + cls.attr("__getstate_manages_dict__") = nb::bool_(true); } static nb::tuple getstate(nb::object obj) { - const T& tobj = nb::cast(obj); - nb::bytes content = serialization_tobytes(tobj); - return nb::make_tuple(content, nb::getattr(obj, "__dict__")); + diffpy::srreal::StructureAdapterPtr adpt = + nb::cast(obj); + nb::object content = nb::none(); + if (frompython(adpt)) + { + const T& tobj = nb::cast(obj); + content = serialization_tobytes(tobj); + } + return nb::make_tuple(content, get_instance_dict(obj)); } @@ -262,15 +483,35 @@ class StructureAdapterPickleSuite nb::object st0 = nb::borrow(state[0]); if (!st0.is_none()) { - T& tobj = ensure_instance_ready(obj); + T& tobj = nb::cast(obj); diffpy::serialization_fromstring(tobj, bytes_to_string(st0)); } // restore the object's __dict__ - nb::object dict_obj = nb::getattr(obj, "__dict__"); - nb::dict d = nb::borrow(dict_obj); - d.update(state[1]); + restore_instance_dict(obj, state[1]); } + static nb::tuple reduce(nb::object obj) + { + diffpy::srreal::StructureAdapterPtr adpt = + nb::cast(obj); + if (frompython(adpt)) + { + return nb::make_tuple( + runtime_type(obj), + nb::make_tuple(), + obj.attr("__getstate__")() + ); + } + + nb::object restore = nb::module_::import_( + "diffpy.srreal.srreal_ext").attr("_restoreStructureAdapter"); + nb::bytes content = serialization_tobytes(adpt); + return nb::make_tuple( + restore, + nb::make_tuple(content), + nb::make_tuple(nb::none(), get_instance_dict(obj)) + ); + } static bool getstate_manages_dict() { return true; } @@ -280,7 +521,7 @@ class StructureAdapterPickleSuite static bool frompython( diffpy::srreal::StructureAdapterPtr adpt) { - return bool(std::dynamic_pointer_cast(adpt)); + return bool(std::dynamic_pointer_cast(adpt)); } }; // class StructureAdapterPickleSuite @@ -310,10 +551,19 @@ createAdapterFromString(const std::string &content) } template -void StructureAdapter_constructor(Adapter* self, const std::string& content) +void StructureAdapter_constructor( + nb::pointer_and_handle self, nb::bytes content) { - std::shared_ptr adpt = createAdapterFromString(content); - new (self) Adapter(*adpt); + if (!runtime_type(self.h).is(nb::type())) + { + throw nb::type_error( + "serialized StructureAdapter constructor is only supported " + "for native extension types" + ); + } + std::shared_ptr adpt = + createAdapterFromString(bytes_to_string(content)); + new (self.p) Adapter(*adpt); } } // namespace srrealmodule diff --git a/src/extensions/srreal_registry.hpp b/src/extensions/srreal_registry.hpp index c3487144..46ab31fc 100644 --- a/src/extensions/srreal_registry.hpp +++ b/src/extensions/srreal_registry.hpp @@ -21,6 +21,7 @@ #include #include +#include namespace nb = nanobind; diff --git a/src/extensions/wrap_AtomRadiiTable.cpp b/src/extensions/wrap_AtomRadiiTable.cpp index b747941d..028efb5b 100644 --- a/src/extensions/wrap_AtomRadiiTable.cpp +++ b/src/extensions/wrap_AtomRadiiTable.cpp @@ -151,7 +151,8 @@ DECLARE_PYDICT_METHOD_WRAPPER(getAllCustom, getAllCustom_asdict) // Helper class for overloads of AtomRadiiTable methods from Python class AtomRadiiTableWrap : - public AtomRadiiTable + public AtomRadiiTable, + public PythonTrampolineTag { public: @@ -269,7 +270,10 @@ void wrap_AtomRadiiTable(nb::module_& m) &AtomRadiiTable::toString, nb::arg("separator")=",", doc_AtomRadiiTable_toString) ; - SerializationPickleSuite::bind(atomradiitable); + SerializationPickleSuite< + AtomRadiiTable, + DICT_PICKLE, + AtomRadiiTableWrap>::bind(atomradiitable); nb::class_ constantradiitable(m, "ConstantRadiiTable", doc_ConstantRadiiTable); diff --git a/src/extensions/wrap_AtomicStructureAdapter.cpp b/src/extensions/wrap_AtomicStructureAdapter.cpp index dd78cf9a..d445602e 100644 --- a/src/extensions/wrap_AtomicStructureAdapter.cpp +++ b/src/extensions/wrap_AtomicStructureAdapter.cpp @@ -325,7 +325,9 @@ void set_uc(Atom& a, nb::object value) // template wrapper class for overloading of clone and _customPQConfig template -class MakeWrapper : public T +class MakeWrapper : + public T, + public PythonTrampolineTag { public: @@ -687,7 +689,9 @@ void wrap_AtomicStructureAdapter(nb::module_& m) .def("reserve", atomadapter_reserve, nb::arg("sz"), doc_AtomicStructureAdapter_reserve) ; - StructureAdapterPickleSuite::bind(adapter_class); + StructureAdapterPickleSuite< + AtomicStructureAdapter, + AtomicStructureAdapterWrap>::bind(adapter_class); // class PeriodicStructureAdapter nb::class_::bind(periodic_class); + StructureAdapterPickleSuite< + PeriodicStructureAdapter, + PeriodicStructureAdapterWrap>::bind(periodic_class); // class CrystalStructureAdapter nb::class_::bind(crystal_class); + StructureAdapterPickleSuite< + CrystalStructureAdapter, + CrystalStructureAdapterWrap>::bind(crystal_class); } diff --git a/src/extensions/wrap_OverlapCalculator.cpp b/src/extensions/wrap_OverlapCalculator.cpp index fadd472e..cde93bdd 100644 --- a/src/extensions/wrap_OverlapCalculator.cpp +++ b/src/extensions/wrap_OverlapCalculator.cpp @@ -18,6 +18,7 @@ #include +#include #include #include "srreal_converters.hpp" @@ -196,18 +197,30 @@ class OverlapCalculatorPickleSuite : cls .def("__getstate__", getstate) .def("__setstate__", setstate) + .def("__reduce__", reduce) ; + cls.attr("__getstate_manages_dict__") = nb::none(); } static nb::tuple getstate(nb::object obj) { - nb::object tb = obj.attr("atomradiitable"); - nb::tuple rv = nb::make_tuple( - Super::getstate(obj), - resolve_state_object(tb) - ); - return rv; + OverlapCalculator& calc = nb::cast(obj); + ScopedSharedPtrReplacement tb( + calc.getAtomRadiiTable(), AtomRadiiTablePtr(new ConstantRadiiTable)); + try + { + nb::tuple rv = nb::make_tuple( + Super::getstate(obj), + tb.state_object() + ); + return rv; + } + catch (...) + { + tb.restore(); + throw; + } } @@ -218,8 +231,14 @@ class OverlapCalculatorPickleSuite : // restore the state using boost serialization nb::tuple state0(state[0]); Super::setstate(obj, state0); - // atomradiitable is present only when restoring Python class - assign_state_object(obj.attr("atomradiitable"), state[1]); + // restore component that was kept out of Boost serialization + OverlapCalculator& calc = nb::cast(obj); + assign_pointer_state(calc.getAtomRadiiTable(), state[1]); + } + + static nb::tuple reduce(nb::object obj) + { + return Super::reduce(obj); } }; diff --git a/src/extensions/wrap_PDFBaseline.cpp b/src/extensions/wrap_PDFBaseline.cpp index 8458f824..bd2daaec 100644 --- a/src/extensions/wrap_PDFBaseline.cpp +++ b/src/extensions/wrap_PDFBaseline.cpp @@ -70,7 +70,8 @@ PDF baseline function equal to (slope * r).\n\ // Helper class allows overload of the PDFBaseline methods from Python. class PDFBaselineWrap : - public PDFBaseline + public PDFBaseline, + public PythonTrampolineTag { public: @@ -178,7 +179,10 @@ void wrap_PDFBaseline(nb::module_& m) .def("__call__", &PDFBaseline::operator(), nb::arg("r"), doc_PDFBaseline___call__) ; - SerializationPickleSuite::bind(pdfbaseline); + SerializationPickleSuite< + PDFBaseline, + DICT_PICKLE, + PDFBaselineWrap>::bind(pdfbaseline); nb::class_ zerobaseline(m, "ZeroBaseline", doc_ZeroBaseline); diff --git a/src/extensions/wrap_PDFCalculators.cpp b/src/extensions/wrap_PDFCalculators.cpp index 71801e4d..ec6faec1 100644 --- a/src/extensions/wrap_PDFCalculators.cpp +++ b/src/extensions/wrap_PDFCalculators.cpp @@ -20,7 +20,10 @@ #include #include - +#include +#include +#include +#include #include "srreal_converters.hpp" #include "srreal_pickling.hpp" @@ -423,32 +426,46 @@ nb::tuple getstate_super(nb::object obj) { // obtain C++ state without PDFEnvelopes nb::object envlps = obj.attr("envelopes"); - obj.attr("clearEnvelopes")(); - assert(nb::len(obj.attr("envelopes")) == 0); - nb::tuple super_state = Super::getstate(obj); - obj.attr("envelopes") = envlps; - assert(nb::len(obj.attr("envelopes")) == nb::len(envlps)); - return nb::make_tuple(super_state); + bool needs_restore = false; + try + { + obj.attr("clearEnvelopes")(); + needs_restore = true; + assert(nb::len(obj.attr("envelopes")) == 0); + nb::tuple super_state = Super::getstate(obj); + obj.attr("envelopes") = envlps; + needs_restore = false; + assert(nb::len(obj.attr("envelopes")) == nb::len(envlps)); + return nb::make_tuple(super_state); + } + catch (...) + { + if (needs_restore) + { + obj.attr("envelopes") = envlps; + } + throw; + } } -nb::tuple getstate_common(nb::object obj) +nb::tuple getstate_common( + nb::object obj, nb::object pwm_state, nb::object sft_state) { - auto resolve_pwm = resolve_state_object; - auto resolve_sft = resolve_state_object; nb::tuple rv = make_tuple( - resolve_pwm(obj.attr("peakwidthmodel")), - resolve_sft(obj.attr("scatteringfactortable")), + pwm_state, + sft_state, obj.attr("envelopes") ); return rv; } -void setstate_common(nb::object obj, nb::tuple state, size_t& pos) +template +void setstate_common(T& calc, nb::object obj, nb::tuple state, size_t& pos) { - assign_state_object(obj.attr("peakwidthmodel"), state[pos++]); - assign_state_object(obj.attr("scatteringfactortable"), state[pos++]); + assign_pointer_state(calc.getPeakWidthModel(), state[pos++]); + assign_pointer_state(calc.getScatteringFactorTable(), state[pos++]); assert(nb::len(obj.attr("envelopes")) == 0); obj.attr("envelopes") = nb::borrow(state[pos++]); } @@ -469,17 +486,38 @@ class DebyePDFCalculatorPickleSuite : cls .def("__getstate__", getstate) .def("__setstate__", setstate) + .def("__reduce__", reduce) ; + cls.attr("__getstate_manages_dict__") = nb::none(); } static nb::tuple getstate(nb::object obj) { - nb::tuple rv( - getstate_super(obj) + - getstate_common(obj) - ); - return rv; + DebyePDFCalculator& calc = nb::cast(obj); + ScopedSharedPtrReplacement pwm( + calc.getPeakWidthModel(), PeakWidthModelPtr(new JeongPeakWidth)); + ScopedSharedPtrReplacement sft( + calc.getScatteringFactorTable(), + ScatteringFactorTablePtr(new SFTXray)); + try + { + nb::tuple rv( + getstate_super(obj) + + getstate_common( + obj, + pwm.state_object(), + sft.state_object() + ) + ); + return rv; + } + catch (...) + { + sft.restore(); + pwm.restore(); + throw; + } } @@ -489,9 +527,15 @@ class DebyePDFCalculatorPickleSuite : // restore the state using boost serialization nb::tuple st0(state[0]); Super::setstate(obj, st0); - // other items are non-None only when restoring Python class + // restore components that were kept out of Boost serialization + DebyePDFCalculator& calc = nb::cast(obj); size_t pos = 1; - setstate_common(obj, state, pos); + setstate_common(calc, obj, state, pos); + } + + static nb::tuple reduce(nb::object obj) + { + return Super::reduce(obj); } }; @@ -511,21 +555,48 @@ class PDFCalculatorPickleSuite : cls .def("__getstate__", getstate) .def("__setstate__", setstate) + .def("__reduce__", reduce) ; + cls.attr("__getstate_manages_dict__") = nb::none(); } static nb::tuple getstate(nb::object obj) { + PDFCalculator& calc = nb::cast(obj); + ScopedSharedPtrReplacement pwm( + calc.getPeakWidthModel(), PeakWidthModelPtr(new JeongPeakWidth)); + ScopedSharedPtrReplacement sft( + calc.getScatteringFactorTable(), + ScatteringFactorTablePtr(new SFTXray)); + ScopedSharedPtrReplacement peakprofile( + calc.getPeakProfile(), PeakProfilePtr(new GaussianProfile)); + ScopedSharedPtrReplacement baseline( + calc.getBaseline(), PDFBaselinePtr(new LinearBaseline)); nb::tuple mystate = make_tuple( - resolve_state_object(obj.attr("peakprofile")), - resolve_state_object(obj.attr("baseline")) + peakprofile.state_object(), + baseline.state_object() ); - nb::tuple rv( - getstate_super(obj) + - getstate_common(obj) + - mystate - ); - return rv; + try + { + nb::tuple rv( + getstate_super(obj) + + getstate_common( + obj, + pwm.state_object(), + sft.state_object() + ) + + mystate + ); + return rv; + } + catch (...) + { + baseline.restore(); + peakprofile.restore(); + sft.restore(); + pwm.restore(); + throw; + } } @@ -535,11 +606,17 @@ class PDFCalculatorPickleSuite : // restore the state using boost serialization nb::tuple st0(state[0]); Super::setstate(obj, st0); - // other items are non-None only when restoring Python class + // restore components that were kept out of Boost serialization + PDFCalculator& calc = nb::cast(obj); size_t pos = 1; - setstate_common(obj, state, pos); - assign_state_object(obj.attr("peakprofile"), state[pos++]); - assign_state_object(obj.attr("baseline"), state[pos++]); + setstate_common(calc, obj, state, pos); + assign_pointer_state(calc.getPeakProfile(), state[pos++]); + assign_pointer_state(calc.getBaseline(), state[pos++]); + } + + static nb::tuple reduce(nb::object obj) + { + return Super::reduce(obj); } }; diff --git a/src/extensions/wrap_PDFEnvelope.cpp b/src/extensions/wrap_PDFEnvelope.cpp index dd5595bc..5ed91011 100644 --- a/src/extensions/wrap_PDFEnvelope.cpp +++ b/src/extensions/wrap_PDFEnvelope.cpp @@ -91,7 +91,8 @@ Returns 0 when x > stepcut.\n\ // Helper class allows overload of the PDFEnvelope methods from Python. class PDFEnvelopeWrap : - public PDFEnvelope + public PDFEnvelope, + public PythonTrampolineTag { public: @@ -210,32 +211,35 @@ void wrap_PDFEnvelope(nb::module_ &m) .def("__call__", &PDFEnvelope::operator(), nb::arg("r"), doc_PDFEnvelope___call__) ; - SerializationPickleSuite::bind(pdfenvelope); + SerializationPickleSuite< + PDFEnvelope, + DICT_PICKLE, + PDFEnvelopeWrap>::bind(pdfenvelope); nb::class_ qresenvelope(m, "QResolutionEnvelope", doc_QResolutionEnvelope); qresenvelope .def(nb::init<>()) ; - SerializationPickleSuite::bind(qresenvelope); + SerializationPickleSuite::bind(qresenvelope); nb::class_ scaleenvelope(m, "ScaleEnvelope", doc_ScaleEnvelope); scaleenvelope .def(nb::init<>()) ; - SerializationPickleSuite::bind(scaleenvelope); + SerializationPickleSuite::bind(scaleenvelope); nb::class_ sphshapeenvelope(m, "SphericalShapeEnvelope", doc_SphericalShapeEnvelope); sphshapeenvelope .def(nb::init<>()) ; - SerializationPickleSuite::bind(sphshapeenvelope); + SerializationPickleSuite::bind(sphshapeenvelope); nb::class_ stepcutenvelope(m, "StepCutEnvelope", doc_StepCutEnvelope); stepcutenvelope .def(nb::init<>()) ; - SerializationPickleSuite::bind(stepcutenvelope); + SerializationPickleSuite::bind(stepcutenvelope); } diff --git a/src/extensions/wrap_PairQuantity.cpp b/src/extensions/wrap_PairQuantity.cpp index 45c33f55..1947e032 100644 --- a/src/extensions/wrap_PairQuantity.cpp +++ b/src/extensions/wrap_PairQuantity.cpp @@ -327,11 +327,21 @@ Reference to the internal vector of total contributions.\n\ // wrappers ------------------------------------------------------------------ -inline nb::bytes string_to_pybytes(const std::string &s) +inline nb::bytes to_bytes(const std::string &s) { return nb::bytes(s.data(), s.size()); } + +inline std::string from_bytes(nb::bytes b) +{ + return std::string( + static_cast(b.data()), + b.size() + ); +} + + // representation of QuantityType objects nb::object repr_QuantityType(const QuantityType& v) { @@ -572,7 +582,8 @@ class PairQuantityExposed : public PairQuantity // methods from Python. class PairQuantityWrap : - public PairQuantityExposed + public PairQuantityExposed, + public PythonTrampolineTag { public: @@ -582,7 +593,18 @@ class PairQuantityWrap : std::string getParallelData() const override { - NB_OVERRIDE_NAME("_getParallelData", getParallelData); + nb::gil_scoped_acquire gil; + nb::detail::ticket ticket( + nb_trampoline, "_getParallelData", false); + + if (ticket.key.is_valid()) + { + nb::object pdata = + nb_trampoline.base().attr(ticket.key)(); + return from_bytes(nb::cast(pdata)); + } + + return this->PairQuantityExposed::getParallelData(); } std::string default_getParallelData() const @@ -666,7 +688,17 @@ class PairQuantityWrap : void executeParallelMerge(const std::string& pdata) override { - NB_OVERRIDE_NAME("_executeParallelMerge", executeParallelMerge, pdata); + nb::gil_scoped_acquire gil; + nb::detail::ticket ticket( + nb_trampoline, "_executeParallelMerge", false); + + if (ticket.key.is_valid()) + { + nb_trampoline.base().attr(ticket.key)(to_bytes(pdata)); + return; + } + + this->PairQuantityExposed::executeParallelMerge(pdata); } void default_executeParallelMerge(const std::string& pdata) @@ -731,10 +763,18 @@ void wrap_PairQuantity(nb::module_& m) doc_BasePairQuantity_eval) .def_prop_ro("value", value_asarray, doc_BasePairQuantity_value) - .def("_mergeParallelData", &PairQuantity::mergeParallelData, + .def("_mergeParallelData", + [](PairQuantity &pq, nb::bytes pdata, int ncpu) + { + pq.mergeParallelData(from_bytes(pdata), ncpu); + }, nb::arg("pdata"), nb::arg("ncpu"), doc_BasePairQuantity__mergeParallelData) - .def("_getParallelData", &PairQuantity::getParallelData, + .def("_getParallelData", + [](const PairQuantity &pq) + { + return to_bytes(pq.getParallelData()); + }, doc_BasePairQuantity__getParallelData) .def("setStructure", [](PairQuantity &pq, nb::object stru) { @@ -788,7 +828,7 @@ void wrap_PairQuantity(nb::module_& m) doc_PairQuantity_ticker) .def("_getParallelData", [](const PairQuantityExposed &pq) { - return string_to_pybytes(pq.getParallelData()); + return to_bytes(pq.getParallelData()); }, doc_PairQuantity__getParallelData) .def("_resizeValue", @@ -807,7 +847,10 @@ void wrap_PairQuantity(nb::module_& m) nb::arg("bnds"), nb::arg("sumscale"), doc_PairQuantity__addPairContribution) .def("_executeParallelMerge", - &PairQuantityExposed::executeParallelMerge, + [](PairQuantityExposed &pq, nb::bytes pdata) + { + pq.executeParallelMerge(from_bytes(pdata)); + }, nb::arg("pdata"), doc_PairQuantity__executeParallelMerge) .def("_finishValue", @@ -828,7 +871,10 @@ void wrap_PairQuantity(nb::module_& m) ; // classes PairQuantityExposed, PairQuantityWrap add no members, // therefore we can create pickle suite from C++ base class. - PairQuantityPickleSuite::bind(pq); + PairQuantityPickleSuite< + PairQuantity, + DICT_PICKLE, + PairQuantityExposed>::bind(pq); } diff --git a/src/extensions/wrap_PeakProfile.cpp b/src/extensions/wrap_PeakProfile.cpp index e59656e6..6100edc4 100644 --- a/src/extensions/wrap_PeakProfile.cpp +++ b/src/extensions/wrap_PeakProfile.cpp @@ -106,7 +106,8 @@ The profile is also rescaled to keep the integrated area of 1.\n\ // Support for override of PeakProfile methods from Python. class PeakProfileWrap : - public PeakProfile + public PeakProfile, + public PythonTrampolineTag { public: @@ -242,7 +243,10 @@ void wrap_PeakProfile(nb::module_& m) doc_PeakProfile_ticker) ; - SerializationPickleSuite::bind(peakprofile); + SerializationPickleSuite< + PeakProfile, + DICT_PICKLE, + PeakProfileWrap>::bind(peakprofile); nb::class_ gaussianprofile(m, "GaussianProfile", doc_GaussianProfile); diff --git a/src/extensions/wrap_PeakWidthModel.cpp b/src/extensions/wrap_PeakWidthModel.cpp index fb8cf6fd..e90f5f32 100644 --- a/src/extensions/wrap_PeakWidthModel.cpp +++ b/src/extensions/wrap_PeakWidthModel.cpp @@ -127,7 +127,8 @@ DECLARE_BYTYPE_SETTER_WRAPPER(setPeakWidthModel, setpwmodel) // Support for Python override of the PeakWidthModel methods. class PeakWidthModelWrap : - public PeakWidthModel + public PeakWidthModel, + public PythonTrampolineTag { public: @@ -262,7 +263,10 @@ void wrap_PeakWidthModel(nb::module_& m) nb::rv_policy::reference_internal, doc_PeakWidthModel_ticker) ; - SerializationPickleSuite::bind(peakwidthmodel); + SerializationPickleSuite< + PeakWidthModel, + DICT_PICKLE, + PeakWidthModelWrap>::bind(peakwidthmodel); nb::class_ constantpeakwidth(m, "ConstantPeakWidth", doc_ConstantPeakWidth); diff --git a/src/extensions/wrap_ScatteringFactorTable.cpp b/src/extensions/wrap_ScatteringFactorTable.cpp index d0036581..ab9a0249 100644 --- a/src/extensions/wrap_ScatteringFactorTable.cpp +++ b/src/extensions/wrap_ScatteringFactorTable.cpp @@ -211,7 +211,8 @@ DECLARE_BYTYPE_SETTER_WRAPPER(setScatteringFactorTable, setsftable) // Helper class to support Python override of ScatteringFactorTable methods class ScatteringFactorTableWrap : - public ScatteringFactorTable + public ScatteringFactorTable, + public PythonTrampolineTag { public: @@ -413,7 +414,10 @@ void wrap_ScatteringFactorTable(nb::module_& m) nb::rv_policy::reference_internal, doc_ScatteringFactorTable_ticker) ; - SerializationPickleSuite::bind(sftb); + SerializationPickleSuite< + ScatteringFactorTable, + DICT_PICKLE, + ScatteringFactorTableWrap>::bind(sftb); nb::class_ sftxray(m, "SFTXray", doc_SFTXray); diff --git a/src/extensions/wrap_StructureAdapter.cpp b/src/extensions/wrap_StructureAdapter.cpp index 8fd5cfda..542dbf74 100644 --- a/src/extensions/wrap_StructureAdapter.cpp +++ b/src/extensions/wrap_StructureAdapter.cpp @@ -258,10 +258,51 @@ nb::object siteCartesianUij_safe(const StructureAdapter& adpt, int i) return siteCartesianUij_asarray(adpt, i); } + +PyObject* restoreStructureAdapter(PyObject*, PyObject* args) +{ + PyObject* content = nullptr; + if (!PyArg_UnpackTuple(args, "_restoreStructureAdapter", 1, 1, &content)) + return nullptr; + if (!PyBytes_Check(content)) + { + PyErr_SetString(PyExc_TypeError, "expected bytes"); + return nullptr; + } + + char* buffer = nullptr; + Py_ssize_t size = 0; + if (PyBytes_AsStringAndSize(content, &buffer, &size) < 0) + return nullptr; + + try + { + StructureAdapterPtr adapter = + createStructureAdapterFromString(std::string(buffer, size)); + nb::object pyadapter = nb::cast(adapter); + return pyadapter.release().ptr(); + } + catch (const std::exception& e) + { + PyErr_SetString(PyExc_RuntimeError, e.what()); + return nullptr; + } +} + + +PyMethodDef restoreStructureAdapterDef = { + "_restoreStructureAdapter", + restoreStructureAdapter, + METH_VARARGS, + "Restore native C++ StructureAdapter from serialized bytes." +}; + + // Helper class necessary for wrapping a pure virtual methods class StructureAdapterWrap : - public StructureAdapter + public StructureAdapter, + public PythonTrampolineTag { public: @@ -460,13 +501,24 @@ class StructureProxyPickleSuite static void bind(C& cls) { cls - .def("__reduce__", [](T& self) + .def("__reduce__", [](nb::object self) { - StructureAdapterPtr src = self.getSourceStructure(); + T& adapter = nb::cast(self); + StructureAdapterPtr src = adapter.getSourceStructure(); + nb::object dict = get_instance_dict(self); + + if (dict.is_none() || nb::len(dict) == 0) + { + return nb::make_tuple( + runtime_type(self), + nb::make_tuple(src) + ); + } return nb::make_tuple( - nb::type(), - nb::make_tuple(src) + runtime_type(self), + nb::make_tuple(src), + nb::make_tuple(nb::none(), dict) ); }) ; @@ -543,7 +595,8 @@ void wrap_StructureAdapter(nb::module_& m) nb::arg("other"), doc_StructureAdapter_diff) ; - StructureAdapterPickleSuite::bind(structureadapter); + StructureAdapterPickleSuite::bind( + structureadapter); typedef std::shared_ptr NoMetaStructureAdapterPtr; @@ -569,6 +622,17 @@ void wrap_StructureAdapter(nb::module_& m) m.def("nosymmetry", nosymmetry, doc_nosymmetry); m.def("_emptyStructureAdapter", emptyStructureAdapter, doc__emptyStructureAdapter); + nb::object module_name = nb::str("diffpy.srreal.srreal_ext"); + PyObject* restore_function = PyCFunction_NewEx( + &restoreStructureAdapterDef, nullptr, module_name.ptr()); + if (!restore_function) + nb::raise_python_error(); + if (PyModule_AddObject(m.ptr(), "_restoreStructureAdapter", + restore_function) < 0) + { + Py_DECREF(restore_function); + nb::raise_python_error(); + } } // Export shared docstrings From 1536c65517a3ae9a050da7ea8614ce28647bf872 Mon Sep 17 00:00:00 2001 From: symscae Date: Fri, 26 Jun 2026 11:53:51 -0700 Subject: [PATCH 18/18] fix tests --- src/diffpy/srreal/bondcalculator.py | 74 ++++++------- src/diffpy/srreal/bvscalculator.py | 78 +++++++------- src/diffpy/srreal/overlapcalculator.py | 74 ++++++------- src/diffpy/srreal/pdfcalculator.py | 72 ++++++------- src/diffpy/srreal/structureconverters.py | 20 ++-- src/extensions/srreal_converters.cpp | 25 +++-- src/extensions/srreal_pickling.hpp | 40 ++++--- src/extensions/wrap_AtomRadiiTable.cpp | 2 +- .../wrap_AtomicStructureAdapter.cpp | 7 +- src/extensions/wrap_Attributes.cpp | 6 +- src/extensions/wrap_BVParametersTable.cpp | 4 +- src/extensions/wrap_PDFBaseline.cpp | 4 +- src/extensions/wrap_PDFCalculators.cpp | 4 +- src/extensions/wrap_PDFEnvelope.cpp | 8 +- src/extensions/wrap_PairQuantity.cpp | 100 ++++++++++++++++-- src/extensions/wrap_PeakProfile.cpp | 11 +- src/extensions/wrap_PeakWidthModel.cpp | 35 +++++- src/extensions/wrap_ScatteringFactorTable.cpp | 15 ++- src/extensions/wrap_StructureAdapter.cpp | 52 ++++++++- tests/testutils.py | 23 ++-- 20 files changed, 420 insertions(+), 234 deletions(-) diff --git a/src/diffpy/srreal/bondcalculator.py b/src/diffpy/srreal/bondcalculator.py index 0fede96e..46959f16 100644 --- a/src/diffpy/srreal/bondcalculator.py +++ b/src/diffpy/srreal/bondcalculator.py @@ -18,12 +18,46 @@ # exported items, these also makes them show in pydoc. __all__ = ["BondCalculator"] -from diffpy.srreal.srreal_ext import BondCalculator +from diffpy.srreal.srreal_ext import BondCalculator as _BondCalculator from diffpy.srreal.wraputils import ( propertyFromExtDoubleAttr, setattrFromKeywordArguments, ) + +class BondCalculator(_BondCalculator): + __doc__ = _BondCalculator.__doc__ + + def __init__(self, **kwargs): + """Create a new instance of BondCalculator. Keyword arguments can be + used to configure calculator properties, for example: + + bdc = BondCalculator(rmin=1.5, rmax=2.5) + + Raise ValueError for invalid keyword argument. + """ + super().__init__() + setattrFromKeywordArguments(self, **kwargs) + return + + def __call__(self, structure=None, **kwargs): + """Return sorted bond distances in the specified structure. + + Attributes + ---------- + structure + structure to be evaluated, an instance of diffpy Structure + or pyobjcryst Crystal. Reuse the last structure when None. + kwargs + optional parameter settings for this calculator + + Return a sorted numpy array. + """ + setattrFromKeywordArguments(self, **kwargs) + self.eval(structure) + return self.distances + + # property wrappers to C++ double attributes BondCalculator.rmin = propertyFromExtDoubleAttr( @@ -39,42 +73,4 @@ ) -# method overrides that support keyword arguments - - -def _init_kwargs(self, **kwargs): - """Create a new instance of BondCalculator. Keyword arguments can be - used to configure calculator properties, for example: - - bdc = BondCalculator(rmin=1.5, rmax=2.5) - - Raise ValueError for invalid keyword argument. - """ - BondCalculator.__boostpython__init(self) - setattrFromKeywordArguments(self, **kwargs) - return - - -def _call_kwargs(self, structure=None, **kwargs): - """Return sorted bond distances in the specified structure. - - Attributes - ---------- - structure - structure to be evaluated, an instance of diffpy Structure - or pyobjcryst Crystal. Reuse the last structure when None. - kwargs - optional parameter settings for this calculator - - Return a sorted numpy array. - """ - setattrFromKeywordArguments(self, **kwargs) - self.eval(structure) - return self.distances - - -BondCalculator.__boostpython__init = BondCalculator.__init__ -BondCalculator.__init__ = _init_kwargs -BondCalculator.__call__ = _call_kwargs - # End of file diff --git a/src/diffpy/srreal/bvscalculator.py b/src/diffpy/srreal/bvscalculator.py index 855cf874..cfccd94a 100644 --- a/src/diffpy/srreal/bvscalculator.py +++ b/src/diffpy/srreal/bvscalculator.py @@ -18,12 +18,48 @@ # exported items __all__ = ["BVSCalculator"] -from diffpy.srreal.srreal_ext import BVSCalculator +from diffpy.srreal.srreal_ext import BVSCalculator as _BVSCalculator from diffpy.srreal.wraputils import ( propertyFromExtDoubleAttr, setattrFromKeywordArguments, ) + +class BVSCalculator(_BVSCalculator): + __doc__ = _BVSCalculator.__doc__ + + def __init__(self, **kwargs): + """Create a new instance of BVSCalculator. + Keyword arguments can be used to configure the calculator properties, + for example: + + bvscalc = BVSCalculator(valenceprecision=0.001) + + Raise ValueError for invalid keyword argument. + """ + super().__init__() + setattrFromKeywordArguments(self, **kwargs) + return + + def __call__(self, structure=None, **kwargs): + """Return bond valence sums at each atom site in the structure. + + Attributes + ---------- + structure + structure to be evaluated, an instance of diffpy Structure + or pyobjcryst Crystal. Reuse the last structure when None. + kwargs + optional parameter settings for this calculator + + Return an array of calculated valence sums. + See valences for the expected values. + """ + setattrFromKeywordArguments(self, **kwargs) + rv = self.eval(structure) + return rv + + # Property wrappers to C++ double attributes BVSCalculator.valenceprecision = propertyFromExtDoubleAttr( @@ -55,44 +91,4 @@ ) -# method overrides that support keyword arguments - - -def _init_kwargs(self, **kwargs): - """Create a new instance of BVSCalculator. - Keyword arguments can be used to configure the calculator properties, - for example: - - bvscalc = BVSCalculator(valenceprecision=0.001) - - Raise ValueError for invalid keyword argument. - """ - BVSCalculator.__boostpython__init(self) - setattrFromKeywordArguments(self, **kwargs) - return - - -def _call_kwargs(self, structure=None, **kwargs): - """Return bond valence sums at each atom site in the structure. - - Attributes - ---------- - structure - structure to be evaluated, an instance of diffpy Structure - or pyobjcryst Crystal. Reuse the last structure when None. - kwargs - optional parameter settings for this calculator - - Return an array of calculated valence sums. - See valences for the expected values. - """ - setattrFromKeywordArguments(self, **kwargs) - rv = self.eval(structure) - return rv - - -BVSCalculator.__boostpython__init = BVSCalculator.__init__ -BVSCalculator.__init__ = _init_kwargs -BVSCalculator.__call__ = _call_kwargs - # End of file diff --git a/src/diffpy/srreal/overlapcalculator.py b/src/diffpy/srreal/overlapcalculator.py index 6abd4b40..fcb356dc 100644 --- a/src/diffpy/srreal/overlapcalculator.py +++ b/src/diffpy/srreal/overlapcalculator.py @@ -19,12 +19,46 @@ # exported items, these also makes them show in pydoc. __all__ = ["OverlapCalculator"] -from diffpy.srreal.srreal_ext import OverlapCalculator +from diffpy.srreal.srreal_ext import OverlapCalculator as _OverlapCalculator from diffpy.srreal.wraputils import ( propertyFromExtDoubleAttr, setattrFromKeywordArguments, ) + +class OverlapCalculator(_OverlapCalculator): + __doc__ = _OverlapCalculator.__doc__ + + def __init__(self, **kwargs): + """Create a new instance of OverlapCalculator. Keyword arguments can + be used to configure calculator properties, for example: + + olc = OverlapCalculator(rmax=2.5) + + Raise ValueError for invalid keyword argument. + """ + super().__init__() + setattrFromKeywordArguments(self, **kwargs) + return + + def __call__(self, structure=None, **kwargs): + """Return siteSquareOverlaps per each site of the structure. + + Attributes + ---------- + structure + structure to be evaluated, an instance of diffpy Structure + or pyobjcryst Crystal. Reuse the last structure when None. + kwargs + optional parameter settings for this calculator + + Return a numpy array. + """ + setattrFromKeywordArguments(self, **kwargs) + self.eval(structure) + return self.sitesquareoverlaps + + # property wrappers to C++ double attributes OverlapCalculator.rmin = propertyFromExtDoubleAttr( @@ -47,42 +81,4 @@ """, ) -# method overrides that support keyword arguments - - -def _init_kwargs(self, **kwargs): - """Create a new instance of OverlapCalculator. Keyword arguments can - be used to configure calculator properties, for example: - - olc = OverlapCalculator(rmax=2.5) - - Raise ValueError for invalid keyword argument. - """ - OverlapCalculator.__boostpython__init(self) - setattrFromKeywordArguments(self, **kwargs) - return - - -def _call_kwargs(self, structure=None, **kwargs): - """Return siteSquareOverlaps per each site of the structure. - - Attributes - ---------- - structure - structure to be evaluated, an instance of diffpy Structure - or pyobjcryst Crystal. Reuse the last structure when None. - kwargs - optional parameter settings for this calculator - - Return a numpy array. - """ - setattrFromKeywordArguments(self, **kwargs) - self.eval(structure) - return self.sitesquareoverlaps - - -OverlapCalculator.__boostpython__init = OverlapCalculator.__init__ -OverlapCalculator.__init__ = _init_kwargs -OverlapCalculator.__call__ = _call_kwargs - # End of file diff --git a/src/diffpy/srreal/pdfcalculator.py b/src/diffpy/srreal/pdfcalculator.py index d4f1c1fb..1cb5e3fa 100644 --- a/src/diffpy/srreal/pdfcalculator.py +++ b/src/diffpy/srreal/pdfcalculator.py @@ -19,8 +19,8 @@ """ from diffpy.srreal.srreal_ext import ( - DebyePDFCalculator, - PDFCalculator, + DebyePDFCalculator as _DebyePDFCalculator, + PDFCalculator as _PDFCalculator, fftftog, fftgtof, ) @@ -194,6 +194,23 @@ def _call_kwargs(self, structure=None, **kwargs): # class DebyePDFCalculator --------------------------------------------------- +class DebyePDFCalculator(_DebyePDFCalculator): + __doc__ = _DebyePDFCalculator.__doc__ + + def __init__(self, **kwargs): + """Create a new instance of the DebyePDFCalculator. + Keyword arguments can be used to configure the calculator properties, + for example: + + dpc = DebyePDFCalculator(qmax=20, rmin=7, rmax=15) + + Raise ValueError for invalid keyword argument. + """ + super().__init__() + setattrFromKeywordArguments(self, **kwargs) + return + + # shared interface of the PDF calculator classes _defineCommonInterface(DebyePDFCalculator) @@ -229,29 +246,26 @@ def _call_kwargs(self, structure=None, **kwargs): See also setOptimumQstep, isOptimumQstep.""", ) -# method overrides to support optional keyword arguments - - -def _init_kwargs0(self, **kwargs): - """Create a new instance of the DebyePDFCalculator. - Keyword arguments can be used to configure the calculator properties, - for example: +# End of class DebyePDFCalculator - dpc = DebyePDFCalculator(qmax=20, rmin=7, rmax=15) +# PDFCalculator -------------------------------------------------------------- - Raise ValueError for invalid keyword argument. - """ - DebyePDFCalculator.__boostpython__init(self) - setattrFromKeywordArguments(self, **kwargs) - return +class PDFCalculator(_PDFCalculator): + __doc__ = _PDFCalculator.__doc__ + def __init__(self, **kwargs): + """Create a new instance of PDFCalculator. + Keyword arguments can be used to configure the calculator properties, + for example: -DebyePDFCalculator.__boostpython__init = DebyePDFCalculator.__init__ -DebyePDFCalculator.__init__ = _init_kwargs0 + pc = PDFCalculator(qmax=20, rmin=7, rmax=15) -# End of class DebyePDFCalculator + Raise ValueError for invalid keyword argument. + """ + super().__init__() + setattrFromKeywordArguments(self, **kwargs) + return -# PDFCalculator -------------------------------------------------------------- # shared interface of the PDF calculator classes @@ -310,26 +324,6 @@ def _init_kwargs0(self, **kwargs): [0 A]""", ) -# method overrides to support optional keyword arguments - - -def _init_kwargs1(self, **kwargs): - """Create a new instance of PDFCalculator. - Keyword arguments can be used to configure the calculator properties, - for example: - - pc = PDFCalculator(qmax=20, rmin=7, rmax=15) - - Raise ValueError for invalid keyword argument. - """ - PDFCalculator.__boostpython__init(self) - setattrFromKeywordArguments(self, **kwargs) - return - - -PDFCalculator.__boostpython__init = PDFCalculator.__init__ -PDFCalculator.__init__ = _init_kwargs1 - # End of class PDFCalculator # End of file diff --git a/src/diffpy/srreal/structureconverters.py b/src/diffpy/srreal/structureconverters.py index 67883f79..b42cbbde 100644 --- a/src/diffpy/srreal/structureconverters.py +++ b/src/diffpy/srreal/structureconverters.py @@ -125,16 +125,22 @@ def _fetchMetadata(self, stru): # end of class _DiffPyStructureMetadata +def _installDiffPyStructureMetadata(cls): + """Install diffpy.structure metadata hooks without multiple inheritance.""" + cls.pdffit = _DiffPyStructureMetadata.pdffit + cls.hasMetadata = staticmethod(_DiffPyStructureMetadata.hasMetadata) + cls._customPQConfig = _DiffPyStructureMetadata._customPQConfig + cls._fetchMetadata = _DiffPyStructureMetadata._fetchMetadata + return cls -class DiffPyStructureAtomicAdapter( - _DiffPyStructureMetadata, AtomicStructureAdapter -): + +@_installDiffPyStructureMetadata +class DiffPyStructureAtomicAdapter(AtomicStructureAdapter): pass -class DiffPyStructurePeriodicAdapter( - _DiffPyStructureMetadata, PeriodicStructureAdapter -): +@_installDiffPyStructureMetadata +class DiffPyStructurePeriodicAdapter(PeriodicStructureAdapter): pass @@ -153,7 +159,7 @@ def _fetchDiffPyStructureData(adpt, stru): from diffpy.srreal.srreal_ext import Atom as AdapterAtom # copy atoms - del adpt[:] + adpt.clear() adpt.reserve(len(stru)) aa = AdapterAtom() for a0 in stru: diff --git a/src/extensions/srreal_converters.cpp b/src/extensions/srreal_converters.cpp index fd1390f4..1225d05e 100644 --- a/src/extensions/srreal_converters.cpp +++ b/src/extensions/srreal_converters.cpp @@ -23,6 +23,7 @@ #include #include #include +#include #include #include @@ -360,10 +361,6 @@ double extractdouble(nb::object obj) /// extract integer with a support for numpy.int types int extractint(nb::object obj) { - int i; - if (nb::try_cast(obj, i)) - return i; - PyObject* pobj = obj.ptr(); if (PyArray_CheckScalar(pobj)) { @@ -372,8 +369,24 @@ int extractint(nb::object obj) throw nb::python_error(); return rv; } - // nothing worked, call default behavior which will raise an exception - return nb::cast(obj); + + PyObject* idx = PyNumber_Index(pobj); + if (!idx) + throw nb::python_error(); + + nb::object idx_obj = nb::steal(idx); + long value = PyLong_AsLong(idx_obj.ptr()); + if (value == -1 && PyErr_Occurred()) + throw nb::python_error(); + + if (value < std::numeric_limits::min() || + value > std::numeric_limits::max()) + { + PyErr_SetString(PyExc_OverflowError, "integer index out of range"); + throw nb::python_error(); + } + + return static_cast(value); } diff --git a/src/extensions/srreal_pickling.hpp b/src/extensions/srreal_pickling.hpp index 495cd338..5f06b448 100644 --- a/src/extensions/srreal_pickling.hpp +++ b/src/extensions/srreal_pickling.hpp @@ -276,9 +276,14 @@ class ScopedSharedPtrReplacement }; -enum {DICT_IGNORE=false, DICT_PICKLE=true}; +enum PickleDictPolicy +{ + DICT_GUARD, + DICT_PICKLE, + DICT_DISCARD +}; -template +template class SerializationPickleSuite { public: @@ -291,7 +296,7 @@ class SerializationPickleSuite .def("__setstate__", setstate) .def("__reduce__", reduce) ; - if constexpr (pickledict) + if constexpr (dictpolicy == DICT_PICKLE) { cls.attr("__getstate_manages_dict__") = nb::bool_(true); } @@ -306,15 +311,17 @@ class SerializationPickleSuite const T& tobj = nb::cast(obj); nb::bytes content = serialization_tobytes(tobj); - bool manages_dict = state_manages_dict(obj, pickledict); - ensure_dict_is_managed_or_empty(obj, manages_dict); - if (manages_dict) + if constexpr (dictpolicy == DICT_PICKLE) { return nb::make_tuple( content, get_instance_dict(obj) ); } + if constexpr (dictpolicy == DICT_GUARD) + { + ensure_dict_is_managed_or_empty(obj, false); + } return nb::make_tuple(content); } @@ -322,15 +329,15 @@ class SerializationPickleSuite static void setstate( nb::object obj, nb::tuple state) { - bool manages_dict = state_manages_dict(obj, pickledict); - ensure_tuple_length(state, manages_dict ? 2 : 1); + ensure_tuple_length( + state, + dictpolicy == DICT_PICKLE ? 2 : 1); // load the C++ object T& tobj = nb::cast(obj); diffpy::serialization_fromstring(tobj, bytes_to_string(state[0])); // restore the object's __dict__ - if (manages_dict) + if constexpr (dictpolicy == DICT_PICKLE) { - ensure_dict_is_managed_or_empty(obj, manages_dict); restore_instance_dict(obj, state[1]); } } @@ -344,18 +351,21 @@ class SerializationPickleSuite ); } - static bool getstate_manages_dict() { return pickledict; } + static bool getstate_manages_dict() + { + return dictpolicy == DICT_PICKLE; + } }; // class SerializationPickleSuite -template +template class PairQuantityPickleSuite : - public SerializationPickleSuite + public SerializationPickleSuite { private: - typedef SerializationPickleSuite Super; + typedef SerializationPickleSuite Super; public: @@ -367,7 +377,7 @@ class PairQuantityPickleSuite : .def("__setstate__", setstate) .def("__reduce__", reduce) ; - if constexpr (pickledict) + if constexpr (dictpolicy == DICT_PICKLE) { cls.attr("__getstate_manages_dict__") = nb::bool_(true); } diff --git a/src/extensions/wrap_AtomRadiiTable.cpp b/src/extensions/wrap_AtomRadiiTable.cpp index 028efb5b..55628619 100644 --- a/src/extensions/wrap_AtomRadiiTable.cpp +++ b/src/extensions/wrap_AtomRadiiTable.cpp @@ -296,7 +296,7 @@ void wrap_AtomRadiiTable(nb::module_& m) &ConstantRadiiTable::getDefault, doc_ConstantRadiiTable_getDefault) ; - SerializationPickleSuite::bind(constantradiitable); + SerializationPickleSuite::bind(constantradiitable); } diff --git a/src/extensions/wrap_AtomicStructureAdapter.cpp b/src/extensions/wrap_AtomicStructureAdapter.cpp index d445602e..88f3c12d 100644 --- a/src/extensions/wrap_AtomicStructureAdapter.cpp +++ b/src/extensions/wrap_AtomicStructureAdapter.cpp @@ -303,7 +303,10 @@ bool get_anisotropy(const Atom& a) void set_anisotropy(Atom& a, nb::object value) { - a.anisotropy = bool(value); + int truth = PyObject_IsTrue(value.ptr()); + if (truth < 0) + nb::raise_python_error(); + a.anisotropy = truth != 0; } @@ -640,7 +643,7 @@ void wrap_AtomicStructureAdapter(nb::module_& m) .def_prop_rw("uc13", get_uc<0, 2>, set_uc<0, 2>, doc_Atom_uijc) .def_prop_rw("uc23", get_uc<1, 2>, set_uc<1, 2>, doc_Atom_uijc) ; - SerializationPickleSuite::bind(atom_class); + SerializationPickleSuite::bind(atom_class); nb::class_(m, "_AtomicStructureAdapterIterator") .def("__iter__", [](AtomAdapterIterator& it) -> AtomAdapterIterator& { diff --git a/src/extensions/wrap_Attributes.cpp b/src/extensions/wrap_Attributes.cpp index fa3cb1ee..1a56a7ed 100644 --- a/src/extensions/wrap_Attributes.cpp +++ b/src/extensions/wrap_Attributes.cpp @@ -178,7 +178,11 @@ void wrap_Attributes(nb::module_& m) { using namespace nswrap_Attributes; // ready for class definition - nb::class_(m, "Attributes", nb::dynamic_attr(), doc_Attributes) + nb::class_( + m, "Attributes", + nb::dynamic_attr(), + nb::is_weak_referenceable(), + doc_Attributes) .def(nb::init<>()) .def("_getDoubleAttr", &Attributes::getDoubleAttr, doc_Attributes__getDoubleAttr) diff --git a/src/extensions/wrap_BVParametersTable.cpp b/src/extensions/wrap_BVParametersTable.cpp index 3bfd24f3..7d4a780f 100644 --- a/src/extensions/wrap_BVParametersTable.cpp +++ b/src/extensions/wrap_BVParametersTable.cpp @@ -298,7 +298,7 @@ void wrap_BVParametersTable(nb::module_& m) .def_rw("B", &BVParam::mB, doc_BVParam_B) .def_rw("ref_id", &BVParam::mref_id, doc_BVParam_ref_id) ; - SerializationPickleSuite::bind(bvparam); + SerializationPickleSuite::bind(bvparam); nb::class_ bvtable(m, "BVParametersTable", doc_BVParametersTable); @@ -387,7 +387,7 @@ void wrap_BVParametersTable(nb::module_& m) .def("getAll", getAll_asset, doc_BVParametersTable_getAll) ; - SerializationPickleSuite::bind(bvtable); + SerializationPickleSuite::bind(bvtable); } diff --git a/src/extensions/wrap_PDFBaseline.cpp b/src/extensions/wrap_PDFBaseline.cpp index bd2daaec..290b4207 100644 --- a/src/extensions/wrap_PDFBaseline.cpp +++ b/src/extensions/wrap_PDFBaseline.cpp @@ -188,12 +188,12 @@ void wrap_PDFBaseline(nb::module_& m) zerobaseline(m, "ZeroBaseline", doc_ZeroBaseline); zerobaseline .def(nb::init<>()); - SerializationPickleSuite::bind(zerobaseline); + SerializationPickleSuite::bind(zerobaseline); nb::class_ linearbaseline(m,"LinearBaseline", doc_LinearBaseline); linearbaseline .def(nb::init<>()); - SerializationPickleSuite::bind(linearbaseline); + SerializationPickleSuite::bind(linearbaseline); } diff --git a/src/extensions/wrap_PDFCalculators.cpp b/src/extensions/wrap_PDFCalculators.cpp index ec6faec1..aeb58dce 100644 --- a/src/extensions/wrap_PDFCalculators.cpp +++ b/src/extensions/wrap_PDFCalculators.cpp @@ -472,7 +472,7 @@ void setstate_common(T& calc, nb::object obj, nb::tuple state, size_t& pos) class DebyePDFCalculatorPickleSuite : - public PairQuantityPickleSuite + public PairQuantityPickleSuite { private: @@ -541,7 +541,7 @@ class DebyePDFCalculatorPickleSuite : class PDFCalculatorPickleSuite : - public PairQuantityPickleSuite + public PairQuantityPickleSuite { private: diff --git a/src/extensions/wrap_PDFEnvelope.cpp b/src/extensions/wrap_PDFEnvelope.cpp index 5ed91011..031435d2 100644 --- a/src/extensions/wrap_PDFEnvelope.cpp +++ b/src/extensions/wrap_PDFEnvelope.cpp @@ -221,25 +221,25 @@ void wrap_PDFEnvelope(nb::module_ &m) qresenvelope .def(nb::init<>()) ; - SerializationPickleSuite::bind(qresenvelope); + SerializationPickleSuite::bind(qresenvelope); nb::class_ scaleenvelope(m, "ScaleEnvelope", doc_ScaleEnvelope); scaleenvelope .def(nb::init<>()) ; - SerializationPickleSuite::bind(scaleenvelope); + SerializationPickleSuite::bind(scaleenvelope); nb::class_ sphshapeenvelope(m, "SphericalShapeEnvelope", doc_SphericalShapeEnvelope); sphshapeenvelope .def(nb::init<>()) ; - SerializationPickleSuite::bind(sphshapeenvelope); + SerializationPickleSuite::bind(sphshapeenvelope); nb::class_ stepcutenvelope(m, "StepCutEnvelope", doc_StepCutEnvelope); stepcutenvelope .def(nb::init<>()) ; - SerializationPickleSuite::bind(stepcutenvelope); + SerializationPickleSuite::bind(stepcutenvelope); } diff --git a/src/extensions/wrap_PairQuantity.cpp b/src/extensions/wrap_PairQuantity.cpp index 1947e032..3b9ef729 100644 --- a/src/extensions/wrap_PairQuantity.cpp +++ b/src/extensions/wrap_PairQuantity.cpp @@ -448,13 +448,42 @@ std::vector parsepairtypes(nb::object smbl) return { nb::cast(smbl) }; } - return nb::cast>(smbl); + if (!isiterable(smbl)) + { + PyErr_SetString( + PyExc_TypeError, + "atom type argument must be a string or iterable of strings"); + throw nb::python_error(); + } + + std::vector rv; + for (nb::handle item : smbl) + { + if (!nb::isinstance(item)) + { + PyErr_SetString( + PyExc_TypeError, + "atom type iterable must contain strings"); + throw nb::python_error(); + } + rv.push_back(nb::cast(item)); + } + return rv; +} + + +bool extractbool(nb::object obj) +{ + int truth = PyObject_IsTrue(obj.ptr()); + if (truth < 0) + nb::raise_python_error(); + return truth != 0; } void mask_all_pairs(PairQuantity& obj, nb::object msk) { - obj.maskAllPairs(nb::cast(msk)); + obj.maskAllPairs(extractbool(msk)); } @@ -463,7 +492,7 @@ void set_pair_mask(PairQuantity& obj, nb::object others) { if (!others.is_none()) mask_all_pairs(obj, others); - bool mask = nb::cast(msk); + bool mask = extractbool(msk); // short circuit for normal call with scalar values if (!isiterable(i) && !isiterable(j)) { @@ -492,7 +521,7 @@ void set_type_mask(PairQuantity& obj, using namespace std; if (!others.is_none()) mask_all_pairs(obj, others); - bool mask = nb::cast(msk); + bool mask = extractbool(msk); std::vector isymbols = parsepairtypes(smbli); std::vector jsymbols = parsepairtypes(smblj); @@ -515,6 +544,20 @@ nb::object pqcopy(nb::object pqobj) return copy(pqobj); } + +nb::object borrowed_bond_generator(BaseBondGenerator& bnds) +{ + return nb::cast(&bnds, nb::rv_policy::reference); +} + + +nb::object borrowed_bond_generator(const BaseBondGenerator& bnds) +{ + return nb::cast( + const_cast(&bnds), + nb::rv_policy::reference); +} + // Helper C++ class for publicizing the protected methods. class PairQuantityExposed : public PairQuantity @@ -661,7 +704,18 @@ class PairQuantityWrap : void configureBondGenerator(BaseBondGenerator& bnds) const override { - NB_OVERRIDE_NAME("_configureBondGenerator", configureBondGenerator, bnds); + nb::gil_scoped_acquire gil; + nb::detail::ticket ticket( + nb_trampoline, "_configureBondGenerator", false); + + if (ticket.key.is_valid()) + { + nb_trampoline.base().attr(ticket.key)( + borrowed_bond_generator(bnds)); + return; + } + + this->default_configureBondGenerator(bnds); } void default_configureBondGenerator(BaseBondGenerator& bnds) const @@ -673,10 +727,19 @@ class PairQuantityWrap : void addPairContribution(const BaseBondGenerator& bnds, int summationscale) override { - NB_OVERRIDE_NAME("_addPairContribution", - addPairContribution, - bnds, - summationscale); + nb::gil_scoped_acquire gil; + nb::detail::ticket ticket( + nb_trampoline, "_addPairContribution", false); + + if (ticket.key.is_valid()) + { + nb_trampoline.base().attr(ticket.key)( + borrowed_bond_generator(bnds), + summationscale); + return; + } + + this->default_addPairContribution(bnds, summationscale); } void default_addPairContribution(const BaseBondGenerator& bnds, @@ -756,7 +819,7 @@ void wrap_PairQuantity(nb::module_& m) .def("__repr__", &repr_QuantityType); nb::class_ - basepq(m, "BasePairQuantity"); + basepq(m, "BasePairQuantity", nb::is_weak_referenceable()); basepq .def(nb::init<>()) .def("eval", eval_asarray, nb::arg("stru")=nb::none(), @@ -817,11 +880,26 @@ void wrap_PairQuantity(nb::module_& m) doc_BasePairQuantity_ticker) .def("copy", pqcopy, doc_BasePairQuantity_copy) + .def("__reduce__", [](nb::object) -> nb::object + { + throw std::runtime_error("cannot pickle BasePairQuantity object"); + }); ; - nb::class_ pq(m, "PairQuantity", doc_PairQuantity); + nb::class_ pq( + m, "PairQuantity", + nb::dynamic_attr(), + nb::is_weak_referenceable(), + doc_PairQuantity); pq .def(nb::init<>()) + .def("ticker", + [](const PairQuantityExposed& obj) + -> diffpy::eventticker::EventTicker& + { + return obj.PairQuantity::ticker(); + }, + nb::rv_policy::reference_internal) .def("ticker", &PairQuantityExposed::ticker, nb::rv_policy::reference_internal, diff --git a/src/extensions/wrap_PeakProfile.cpp b/src/extensions/wrap_PeakProfile.cpp index 6100edc4..e0524710 100644 --- a/src/extensions/wrap_PeakProfile.cpp +++ b/src/extensions/wrap_PeakProfile.cpp @@ -237,6 +237,13 @@ void wrap_PeakProfile(nb::module_& m) nb::arg("fwhm"), doc_PeakProfile_xboundlo) .def("xboundhi", &PeakProfile::xboundhi, nb::arg("fwhm"), doc_PeakProfile_xboundhi) + .def("ticker", + [](const PeakProfile& obj) + -> diffpy::eventticker::EventTicker& + { + return obj.PeakProfile::ticker(); + }, + nb::rv_policy::reference_internal) .def("ticker", &PeakProfile::ticker, nb::rv_policy::reference_internal, @@ -253,14 +260,14 @@ void wrap_PeakProfile(nb::module_& m) gaussianprofile .def(nb::init<>()) ; - SerializationPickleSuite::bind(gaussianprofile); + SerializationPickleSuite::bind(gaussianprofile); nb::class_ croppedgaussianprofile(m, "CroppedGaussianProfile", doc_CroppedGaussianProfile); croppedgaussianprofile .def(nb::init<>()) ; - SerializationPickleSuite::bind(croppedgaussianprofile); + SerializationPickleSuite::bind(croppedgaussianprofile); } diff --git a/src/extensions/wrap_PeakWidthModel.cpp b/src/extensions/wrap_PeakWidthModel.cpp index e90f5f32..3f1783a1 100644 --- a/src/extensions/wrap_PeakWidthModel.cpp +++ b/src/extensions/wrap_PeakWidthModel.cpp @@ -115,6 +115,14 @@ double maxwidthwithpystructure(const PeakWidthModel& pwm, return rv; } + +nb::object borrowed_bond_generator(const BaseBondGenerator& bnds) +{ + return nb::cast( + const_cast(&bnds), + nb::rv_policy::reference); +} + // wrappers for the peakwidthmodel property PeakWidthModelPtr getpwmodel(PeakWidthModelOwner& obj) @@ -178,7 +186,19 @@ class PeakWidthModelWrap : double calculate(const BaseBondGenerator& bnds) const override { - NB_OVERRIDE_PURE(calculate, bnds); + nb::gil_scoped_acquire gil; + nb::detail::ticket ticket(nb_trampoline, "calculate", true); + + if (!ticket.key.is_valid()) + { + throw nb::type_error( + "pure virtual method PeakWidthModel.calculate() called" + ); + } + + nb::object pybnds = borrowed_bond_generator(bnds); + return nb::cast( + nb_trampoline.base().attr(ticket.key)(pybnds)); } double maxWidth(StructureAdapterPtr stru, @@ -258,6 +278,13 @@ void wrap_PeakWidthModel(nb::module_& m) .def("maxWidth", maxwidthwithpystructure, nb::arg("stru"), nb::arg("rmin"), nb::arg("rmax")) + .def("ticker", + [](const PeakWidthModel& obj) + -> diffpy::eventticker::EventTicker& + { + return obj.PeakWidthModel::ticker(); + }, + nb::rv_policy::reference_internal) .def("ticker", &PeakWidthModel::ticker, nb::rv_policy::reference_internal, @@ -273,21 +300,21 @@ void wrap_PeakWidthModel(nb::module_& m) constantpeakwidth .def(nb::init<>()) ; - SerializationPickleSuite::bind(constantpeakwidth); + SerializationPickleSuite::bind(constantpeakwidth); nb::class_ debywallerpeakwidth(m, "DebyeWallerPeakWidth", doc_DebyeWallerPeakWidth); debywallerpeakwidth .def(nb::init<>()) ; - SerializationPickleSuite::bind(debywallerpeakwidth); + SerializationPickleSuite::bind(debywallerpeakwidth); nb::class_ jeongpeakwidth(m, "JeongPeakWidth", doc_JeongPeakWidth); jeongpeakwidth .def(nb::init<>()) ; - SerializationPickleSuite::bind(jeongpeakwidth); + SerializationPickleSuite::bind(jeongpeakwidth); nb::class_(m, "PeakWidthModelOwner", doc_PeakWidthModelOwner) .def(nb::init<>()) diff --git a/src/extensions/wrap_ScatteringFactorTable.cpp b/src/extensions/wrap_ScatteringFactorTable.cpp index ab9a0249..46c68179 100644 --- a/src/extensions/wrap_ScatteringFactorTable.cpp +++ b/src/extensions/wrap_ScatteringFactorTable.cpp @@ -409,6 +409,13 @@ void wrap_ScatteringFactorTable(nb::module_& m) doc_ScatteringFactorTable_resetAll) .def("getCustomSymbols", getCustomSymbols_asset, doc_ScatteringFactorTable_getCustomSymbols) + .def("ticker", + [](const ScatteringFactorTable& obj) + -> diffpy::eventticker::EventTicker& + { + return obj.ScatteringFactorTable::ticker(); + }, + nb::rv_policy::reference_internal) .def("ticker", &ScatteringFactorTable::ticker, nb::rv_policy::reference_internal, @@ -424,28 +431,28 @@ void wrap_ScatteringFactorTable(nb::module_& m) sftxray .def(nb::init<>()) ; - SerializationPickleSuite::bind(sftxray); + SerializationPickleSuite::bind(sftxray); nb::class_ sftelectron(m, "SFTElectron", doc_SFTElectron); sftelectron .def(nb::init<>()) ; - SerializationPickleSuite::bind(sftelectron); + SerializationPickleSuite::bind(sftelectron); nb::class_ sftneutron(m, "SFTNeutron", doc_SFTNeutron); sftneutron .def(nb::init<>()) ; - SerializationPickleSuite::bind(sftneutron); + SerializationPickleSuite::bind(sftneutron); nb::class_ sftelectronnumber(m, "SFTElectronNumber", doc_SFTElectronNumber); sftelectronnumber .def(nb::init<>()) ; - SerializationPickleSuite::bind(sftelectronnumber); + SerializationPickleSuite::bind(sftelectronnumber); nb::class_(m, "ScatteringFactorTableOwner", doc_ScatteringFactorTableOwner) diff --git a/src/extensions/wrap_StructureAdapter.cpp b/src/extensions/wrap_StructureAdapter.cpp index 542dbf74..c2ec30bd 100644 --- a/src/extensions/wrap_StructureAdapter.cpp +++ b/src/extensions/wrap_StructureAdapter.cpp @@ -492,6 +492,48 @@ class StructureAdapterWrap : }; // class StructureAdapterWrap +double numberDensity_dispatch(const StructureAdapter& adpt) +{ + const StructureAdapterWrap *wrap = + dynamic_cast(&adpt); + return wrap ? wrap->default_numberDensity() : adpt.numberDensity(); +} + + +const std::string& siteAtomType_dispatch(const StructureAdapter& adpt, int i) +{ + const StructureAdapterWrap *wrap = + dynamic_cast(&adpt); + return wrap ? wrap->default_siteAtomType(i) : siteAtomType_safe(adpt, i); +} + + +int siteMultiplicity_dispatch(const StructureAdapter& adpt, int i) +{ + const StructureAdapterWrap *wrap = + dynamic_cast(&adpt); + return wrap ? + wrap->default_siteMultiplicity(i) : + siteMultiplicity_safe(adpt, i); +} + + +double siteOccupancy_dispatch(const StructureAdapter& adpt, int i) +{ + const StructureAdapterWrap *wrap = + dynamic_cast(&adpt); + return wrap ? + wrap->default_siteOccupancy(i) : + siteOccupancy_safe(adpt, i); +} + + +BaseBondGeneratorPtr createBondGenerator_shared(StructureAdapterPtr adpt) +{ + return adpt->createBondGenerator(); +} + + template class StructureProxyPickleSuite { @@ -559,26 +601,26 @@ void wrap_StructureAdapter(nb::module_& m) &StructureAdapter::clone, doc_StructureAdapter_clone) .def("createBondGenerator", - &StructureAdapter::createBondGenerator, + createBondGenerator_shared, doc_StructureAdapter_createBondGenerator) .def("countSites", &StructureAdapter::countSites, doc_StructureAdapter_countSites) .def("totalOccupancy", &StructureAdapter::totalOccupancy, doc_StructureAdapter_totalOccupancy) - .def("numberDensity", &StructureAdapter::numberDensity, + .def("numberDensity", numberDensity_dispatch, doc_StructureAdapter_numberDensity) .def("siteAtomType", - siteAtomType_safe, + siteAtomType_dispatch, doc_StructureAdapter_siteAtomType) .def("siteCartesianPosition", siteCartesianPosition_safe, doc_StructureAdapter_siteCartesianPosition) .def("siteMultiplicity", - siteMultiplicity_safe, + siteMultiplicity_dispatch, doc_StructureAdapter_siteMultiplicity) .def("siteOccupancy", - siteOccupancy_safe, + siteOccupancy_dispatch, doc_StructureAdapter_siteOccupancy) .def("siteAnisotropy", siteAnisotropy_safe, diff --git a/tests/testutils.py b/tests/testutils.py index cee8d01f..5662f378 100644 --- a/tests/testutils.py +++ b/tests/testutils.py @@ -83,9 +83,17 @@ class HasCustomPQConfig(object): def _customPQConfig(self, pqobj): self.cpqcount += 1 return + +def _installCustomPQConfig(cls): + """Install the test custom-PQ hook without using a mixin base.""" + cls.cpqcount = HasCustomPQConfig.cpqcount + cls._customPQConfig = HasCustomPQConfig._customPQConfig + return cls -class DerivedStructureAdapter(HasCustomPQConfig, StructureAdapter): + +@_installCustomPQConfig +class DerivedStructureAdapter(StructureAdapter): def __init__(self): StructureAdapter.__init__(self) @@ -138,19 +146,18 @@ def _checkindex(self, idx): # End of class DerivedStructureAdapter -class DerivedAtomicStructureAdapter(HasCustomPQConfig, AtomicStructureAdapter): +@_installCustomPQConfig +class DerivedAtomicStructureAdapter(AtomicStructureAdapter): pass -class DerivedPeriodicStructureAdapter( - HasCustomPQConfig, PeriodicStructureAdapter -): +@_installCustomPQConfig +class DerivedPeriodicStructureAdapter(PeriodicStructureAdapter): pass -class DerivedCrystalStructureAdapter( - HasCustomPQConfig, CrystalStructureAdapter -): +@_installCustomPQConfig +class DerivedCrystalStructureAdapter(CrystalStructureAdapter): pass