Skip to content
Closed
Show file tree
Hide file tree
Changes from 1 commit
Commits
Show all changes
25 commits
Select commit Hold shift + click to select a range
cb9bcfc
updated cpp version and replaced boost smart pointer usages
SYMSCAE Jun 24, 2026
a77b086
added nanobind source to build system
SYMSCAE Jun 24, 2026
107ea7c
ran docformatter
SYMSCAE Jun 24, 2026
330e612
update precommit deps
SYMSCAE Jun 25, 2026
70778b9
fix docformatter conflicts
SYMSCAE Jun 26, 2026
790eff8
Merge pull request #87 from SYMSCAE/precommit-deps
sbillinge Jun 26, 2026
5094a2a
Merge branch 'main' into nanobind-deps
SYMSCAE Jun 26, 2026
63006a8
news
SYMSCAE Jun 26, 2026
71ddfd5
migrated syntax for srreal_converters and srreal_validators
SYMSCAE Jun 24, 2026
50e706b
migrated syntax for srreal_ext and srreal_registry
SYMSCAE Jun 24, 2026
eebb59d
reworked pickling and migrated syntax for wrap_AtomicStructureAdapter
SYMSCAE Jun 25, 2026
49430b4
migrated bindings for AtomRadiiTable, Attributes, BaseBondGenerator a…
SYMSCAE Jun 25, 2026
774b475
migrated bindings for BVParametersTable, BVSCalculator, EventTicker a…
SYMSCAE Jun 25, 2026
3920040
migrated bindings for ObjCrystAdapters, OverlapCalculator, PairQuanti…
SYMSCAE Jun 25, 2026
0066c2b
migrated bindings for PDFCalculators and PDFenvelope
SYMSCAE Jun 25, 2026
7f55adb
migrated bindings for PeakProfile, PeakWidthModel and ScatteringFacto…
SYMSCAE Jun 25, 2026
9fe4a79
migrated bindings for StructureAdapter and StructureDifference
SYMSCAE Jun 25, 2026
25c1e74
fix build
SYMSCAE Jun 25, 2026
ee7ef63
restore default constructors
SYMSCAE Jun 26, 2026
a56ab24
corrected code behaviors
SYMSCAE Jun 26, 2026
c4f74cd
restore PDFCalculator behavior
SYMSCAE Jun 26, 2026
334cfcd
reworked pickling system
SYMSCAE Jun 26, 2026
72deaae
fix tests
SYMSCAE Jun 26, 2026
711a416
ran docformatter
SYMSCAE Jul 7, 2026
8ef7657
cleanup boost imports
SYMSCAE Jul 8, 2026
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Prev Previous commit
Next Next commit
reworked pickling and migrated syntax for wrap_AtomicStructureAdapter
  • Loading branch information
SYMSCAE committed Jul 7, 2026
commit eebb59d96b9d6878787fba11c546ec919378e3ee
12 changes: 0 additions & 12 deletions src/extensions/srreal_pickling.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -21,9 +21,7 @@
#include <diffpy/srreal/StructureAdapter.hpp>

namespace srrealmodule {
namespace {

using diffpy::srreal::StructureAdapterPtr;

StructureAdapterPtr
createStructureAdapterFromString(const std::string& content)
Expand All @@ -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
248 changes: 149 additions & 99 deletions src/extensions/srreal_pickling.hpp

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This is a very early port with only syntax checks performed. The pickle suite naming is left but underneath it is handled differently, the final ways of pickling depends on migration for rest of the files.

Original file line number Diff line number Diff line change
Expand Up @@ -19,106 +19,143 @@
#ifndef SRREAL_PICKLING_HPP_INCLUDED
#define SRREAL_PICKLING_HPP_INCLUDED

#include <boost/python/class.hpp>
#include <boost/python/tuple.hpp>
#include <boost/python/dict.hpp>
#include <boost/python/str.hpp>
#include <boost/python/operators.hpp>
#include <nanobind/nanobind.h>

#include <string>
#include <sstream>

#include <diffpy/serialization.hpp>
#include <diffpy/srreal/forwardtypes.hpp>

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 <typename T>
::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 <typename H>
std::string bytes_to_string(const H& h)
{
nb::bytes py_content(h);
return std::string(
static_cast<const char *>(py_content.data()),
py_content.size()
);
}


template <class T>
bool is_wrapper(::boost::python::object& obj)
bool is_wrapper(nb::object& obj)
{
T* p = ::boost::python::extract<T*>(obj);
typedef ::boost::python::wrapper<T> W;
bool rv = (dynamic_cast<W*>(p) != NULL);
return rv;
T* p = nullptr;
return nb::try_cast<T*>(obj, p, false) && p;
}


template <class T>
::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<T>(value)) rv = value;
return rv;
return is_wrapper<T>(value) ? value : nb::none();
}


template <class T>
void assign_state_object(T target, ::boost::python::object value)
void assign_state_object(T target, nb::object value)
{
if (!value.is_none())>
if (!value.is_none())>
}


template <class T>
void construct_for_unpickle(T* tobj)
{
new (tobj) T();
}


template <class T>
T& ensure_instance_ready(nb::handle obj)
{
T* tobj = nb::inst_ptr<T>(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 T, bool pickledict=DICT_IGNORE>
class SerializationPickleSuite : public boost::python::pickle_suite
class SerializationPickleSuite
{
public:

static boost::python::tuple getstate(boost::python::object obj)
template <typename C>
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<const T&>(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<const T&>(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<T&>(obj);
int statelen = pickledict ? 2 : 1;
constexpr int statelen = pickledict ? 2 : 1;
ensure_tuple_length(state, statelen);
// load the C++ object
string content = extract<string>(state[0]);
diffpy::serialization_fromstring(tobj, content);
T& tobj = ensure_instance_ready<T>(obj);
diffpy::serialization_fromstring(tobj, bytes_to_string(state[0]));
// restore the object's __dict__
if (pickledict)
if constexpr (pickledict)
{
dict d = extract<dict>(obj.attr("__dict__"));
nb::object dict_obj = nb::getattr(obj, "__dict__");
nb::dict d = nb::borrow<nb::dict>(dict_obj);
d.update(state[1]);
}
}
Expand All @@ -138,101 +175,87 @@ class PairQuantityPickleSuite :

public:

static boost::python::tuple getstate(boost::python::object obj)
template <typename C>
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<T&>(obj);
T& pq = ensure_instance_ready<T>(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<tuple>(state[0]);
Super::setstate(obj, st0);
nb::tuple state0(state[0]);
Super::setstate(obj, state0);
// restore the structure object
StructureAdapterPtr pstru = extract<StructureAdapterPtr>(state[1]);
T& pq = extract<T&>(obj);
StructureAdapterPtr pstru = nb::cast<StructureAdapterPtr>(state[1]);
T& pq = ensure_instance_ready<T>(obj);
replacePairQuantityStructure(pq, pstru);
}

}; // class PairQuantityPickleSuite


template <class T>
class StructureAdapterPickleSuite : public boost::python::pickle_suite
class StructureAdapterPickleSuite
{
public:

static boost::python::tuple getinitargs(
diffpy::srreal::StructureAdapterPtr adpt)
template <typename C>
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<StructureAdapterPtr>(obj);
python::object content;
// Store serialization data for a Python-built object
if (frompython(adpt))
{
const T& tobj = boost::python::extract<const T&>(obj);
content = serialization_tobytes(tobj);
}
python::tuple rv =
python::make_tuple(content, obj.attr("__dict__"));
return rv;
const T& tobj = nb::cast<const T&>(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<nb::object>(state[0]);
if (!st0.is_none())
{
T& tobj = extract<T&>(obj);
string content = extract<string>(st0);
diffpy::serialization_fromstring(tobj, content);
T& tobj = ensure_instance_ready<T>(obj);
diffpy::serialization_fromstring(tobj, bytes_to_string(st0));
}
// restore the object's __dict__
dict d = extract<dict>(obj.attr("__dict__"));
nb::object dict_obj = nb::getattr(obj, "__dict__");
nb::dict d = nb::borrow<nb::dict>(dict_obj);
d.update(state[1]);
}

Expand All @@ -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 <class Adapter>
std::shared_ptr<Adapter>
createAdapterFromString(const std::string &content)
{
StructureAdapterPtr base = createStructureAdapterFromString(content);

auto rv = std::dynamic_pointer_cast<Adapter>(base);
if (!rv)
{
throw nb::type_error(
"serialized content does not contain the requested adapter type"
);
}

return rv;
}

template <class Adapter>
void StructureAdapter_constructor(Adapter* self, const std::string& content)
{
std::shared_ptr<Adapter> adpt = createAdapterFromString<Adapter>(content);
new (self) Adapter(*adpt);
}

} // namespace srrealmodule

Expand Down
Loading