low-level support for python copy and pickle modules

This commit is contained in:
David Rose 2009-12-10 01:09:06 +00:00
parent 3e82b3d163
commit 816ddc0387
29 changed files with 1221 additions and 295 deletions

View File

@ -302,6 +302,30 @@ Filename(const Filename &dirname, const Filename &basename) {
}
}
#ifdef HAVE_PYTHON
////////////////////////////////////////////////////////////////////
// Function: Filename::__reduce__
// Access: Published
// Description: This special Python method is implement to provide
// support for the pickle module.
////////////////////////////////////////////////////////////////////
PyObject *Filename::
__reduce__(PyObject *self) const {
// We should return at least a 2-tuple, (Class, (args)): the
// necessary class object whose constructor we should call
// (e.g. this), and the arguments necessary to reconstruct this
// object.
PyObject *this_class = PyObject_Type(self);
if (this_class == NULL) {
return NULL;
}
PyObject *result = Py_BuildValue("(O(s))", this_class, c_str());
Py_DECREF(this_class);
return result;
}
#endif // HAVE_PYTHON
////////////////////////////////////////////////////////////////////
// Function: Filename::from_os_specific
// Access: Published, Static

View File

@ -66,6 +66,10 @@ PUBLISHED:
Filename(const Filename &dirname, const Filename &basename);
INLINE ~Filename();
#ifdef HAVE_PYTHON
PyObject *__reduce__(PyObject *self) const;
#endif
// Static constructors to explicitly create a filename that refers
// to a text or binary file. This is in lieu of calling set_text()
// or set_binary() or set_type().

View File

@ -297,6 +297,16 @@ make_wrapper_entry(FunctionIndex function_index) {
assert(!iwrapper._parameters.empty());
iwrapper._parameters.front()._parameter_flags |=
InterrogateFunctionWrapper::PF_is_this;
if (_parameters.size() >= 2 && _parameters[1]._name == "self" &&
TypeManager::is_pointer_to_PyObject(_parameters[1]._remap->get_orig_type())) {
// Here's a special case. If the first parameter of a nonstatic
// method is a PyObject * called "self", then we will
// automatically fill it in from the this pointer, and remove it
// from the generated parameter list.
_parameters.erase(_parameters.begin() + 1);
_flags |= F_explicit_self;
}
}
if (!_void_return) {
@ -382,6 +392,14 @@ get_call_str(const string &container, const vector_string &pexprs) const {
}
call << "(";
if (_flags & F_explicit_self) {
// Pass on the PyObject * that we stripped off above.
call << "self";
if (_first_true_parameter < (int)_parameters.size()) {
call << ", ";
}
}
int pn = _first_true_parameter;
if (pn < (int)_parameters.size()) {
_parameters[pn]._remap->pass_parameter(call, get_parameter_expr(pn, pexprs));
@ -630,6 +648,23 @@ setup_properties(const InterrogateFunction &ifunc, InterfaceMaker *interface_mak
// It receives no parameters, and returns an integer.
_flags |= F_size;
}
} else if (fname == "make_copy" ) {
if (_has_this && _parameters.size() == 1 &&
TypeManager::is_pointer(_return_type->get_new_type())) {
// It receives no parameters, and returns a pointer.
_flags |= F_make_copy;
}
}
} else if (_type == T_constructor) {
if (!_has_this && _parameters.size() == 1) {
if (TypeManager::unwrap(_parameters[0]._remap->get_orig_type()) ==
TypeManager::unwrap(_return_type->get_orig_type())) {
// If this is the only parameter, and it's the same as the
// "this" type, this is a copy constructor.
_flags |= F_copy_constructor;
}
}
}

View File

@ -84,6 +84,9 @@ public:
F_size = 0x0004,
F_setitem = 0x0008,
F_setitem_int = 0x0010,
F_make_copy = 0x0020,
F_copy_constructor = 0x0040,
F_explicit_self = 0x0080,
};
typedef vector<Parameter> Parameters;

View File

@ -123,6 +123,10 @@ check_protocols() {
int flags = 0;
Functions::const_iterator fi;
for (fi = _constructors.begin(); fi != _constructors.end(); ++fi) {
Function *func = (*fi);
flags |= func->_flags;
}
for (fi = _methods.begin(); fi != _methods.end(); ++fi) {
Function *func = (*fi);
flags |= func->_flags;
@ -140,6 +144,16 @@ check_protocols() {
_protocol_types |= PT_mapping;
}
if (flags & FunctionRemap::F_make_copy) {
// It's not exactly a protocol, but if we have a make_copy()
// method, we can use it to synthesize a __copy__ and __deepcopy__
// Python method to support the copy module.
_protocol_types |= PT_make_copy;
} else if (flags & FunctionRemap::F_copy_constructor) {
// Ditto for the copy constructor.
_protocol_types |= PT_copy_constructor;
}
// Now are there any make_seq requests within this class?
CPPStructType *stype = _itype._cpptype->as_struct_type();
if (stype != (CPPStructType *)NULL) {

View File

@ -112,6 +112,8 @@ public:
enum ProtocolTypes {
PT_sequence = 0x0001,
PT_mapping = 0x0002,
PT_make_copy = 0x0004,
PT_copy_constructor = 0x0008,
};
int _protocol_types;
};

View File

@ -98,6 +98,9 @@ RenameSet methodRenameDictionary[] = {
{ "operator >>=" , "__irshift__", 1 },
{ "operator typecast bool", "__nonzero__", 0 },
{ "__nonzero__" , "__nonzero__", 0 },
{ "__reduce__" , "__reduce__", 0 },
{ "__copy__" , "__copy__", 0 },
{ "__deepcopy__" , "__deepcopy__", 0 },
{ "print" , "Cprint", 0 },
{ "CInterval.set_t", "_priv__cSetT", 0 },
{ NULL, NULL, -1 }
@ -186,7 +189,9 @@ const char * pythonKeywords[] = {
"finally",
"in",
"print",
NULL};
NULL
};
///////////////////////////////////////////////////////////////////////////////////////
std::string checkKeyword(std::string & cppName)
{
@ -850,8 +855,8 @@ void InterfaceMakerPythonNative::write_functions(ostream &out)
}
}
inside_python_native = true;
}
////////////////////////////////////////////////////////////
// Function : write_ClasseDetails
////////////////////////////////////////////////////////////
@ -990,8 +995,8 @@ void InterfaceMakerPythonNative::write_ClasseDetails(ostream &out, Object * obj)
out << "}\n";
}
}
////////////////////////////////////////////////////////////
/// Function : write_ClasseDeclarations
//
@ -1215,9 +1220,18 @@ write_module_class(ostream &out, Object *obj) {
std::map<Function *, std::string > normal_Operator_functions;
std::map<Function *, SlottedFunctionDef> wraped_Operator_functions;
// function Table
bool got_copy = false;
bool got_deepcopy = false;
int x;
for (x = 0, fi = obj->_methods.begin(); fi != obj->_methods.end(); ++fi,x++) {
Function *func = (*fi);
if (func->_name == "__copy__") {
got_copy = true;
} else if (func->_name == "__deepcopy__") {
got_deepcopy = true;
}
SlottedFunctionDef slotted_def;
if (!get_slotted_function_def(obj, func, slotted_def)) {
out << " { \"" << methodNameFromCppName(func,export_calss_name) << "\",(PyCFunction ) &"
@ -1248,6 +1262,22 @@ write_module_class(ostream &out, Object *obj) {
}
}
if (obj->_protocol_types & Object::PT_make_copy) {
if (!got_copy) {
out << " { \"__copy__\", (PyCFunction)&copy_from_make_copy, METH_NOARGS, NULL},\n";
got_copy = true;
}
} else if (obj->_protocol_types & Object::PT_copy_constructor) {
if (!got_copy) {
out << " { \"__copy__\", (PyCFunction)&copy_from_copy_constructor, METH_NOARGS, NULL},\n";
got_copy = true;
}
}
if (got_copy && !got_deepcopy) {
out << " { \"__deepcopy__\", (PyCFunction)&map_deepcopy_to_copy, METH_VARARGS, NULL},\n";
}
MakeSeqs::iterator msi;
for (msi = obj->_make_seqs.begin(); msi != obj->_make_seqs.end(); ++msi) {
out << " { \""

View File

@ -708,6 +708,42 @@ PyObject *make_list_for_item(PyObject *self, const char *num_name,
return list;
}
////////////////////////////////////////////////////////////////////
// Function: copy_from_make_copy
// Description: This is a support function for a synthesized
// __copy__() method from a C++ make_copy() method.
////////////////////////////////////////////////////////////////////
PyObject *copy_from_make_copy(PyObject *self) {
return PyObject_CallMethod(self, (char *)"makeCopy", (char *)"()");
}
////////////////////////////////////////////////////////////////////
// Function: copy_from_make_copy
// Description: This is a support function for a synthesized
// __copy__() method from a C++ copy constructor.
////////////////////////////////////////////////////////////////////
PyObject *copy_from_copy_constructor(PyObject *self) {
PyObject *this_class = PyObject_Type(self);
if (this_class == NULL) {
return NULL;
}
PyObject *result = PyObject_CallFunction(this_class, (char *)"(O)", self);
Py_DECREF(this_class);
return result;
}
////////////////////////////////////////////////////////////////////
// Function: copy_from_make_copy
// Description: This is a support function for a synthesized
// __deepcopy__() method for any class that has a
// __copy__() method. The sythethic method simply
// invokes __copy__().
////////////////////////////////////////////////////////////////////
PyObject *map_deepcopy_to_copy(PyObject *self, PyObject *args) {
return PyObject_CallMethod(self, (char *)"__copy__", (char *)"()");
}
////////////////////////////////////////////////////////////////////
// Function: PyLongOrInt_FromUnsignedLong
// Description: Similar to PyLong_FromUnsignedLong(), but returns

View File

@ -174,10 +174,11 @@ struct Dtool_PyTypedObject {
////////////////////////////////////////////////////////////////////////
// Macro's from Hell.. May want to Just Add this to the Code generator..
// Macros from Hell.. May want to just add this to the code generator..
////////////////////////////////////////////////////////////////////////
#define Define_Dtool_Class(MODULE_NAME, CLASS_NAME, PUBLIC_NAME) \
static PyNumberMethods Dtool_PyNumberMethods_##CLASS_NAME ={\
static PyNumberMethods Dtool_PyNumberMethods_##CLASS_NAME = \
{ \
0,/*binaryfunc nb_add*/ \
0,/*binaryfunc nb_subtract*/ \
0,/*binaryfunc nb_multiply*/ \
@ -217,7 +218,8 @@ static PyNumberMethods Dtool_PyNumberMethods_##CLASS_NAME ={\
0,/*binaryfunc nb_inplace_floor_divide*/ \
0,/*binaryfunc nb_inplace_true_divide*/ \
}; \
static PySequenceMethods Dtool_PySequenceMethods_##CLASS_NAME ={\
static PySequenceMethods Dtool_PySequenceMethods_##CLASS_NAME = \
{ \
0,/*lenfunc sq_length */ \
0,/*binaryfunc sq_concat */ \
0,/*ssizeargfunc sq_repeat */ \
@ -227,19 +229,21 @@ static PySequenceMethods Dtool_PySequenceMethods_##CLASS_NAME ={\
0,/*binaryfunc sq_inplace_concat */ \
0,/*ssizeargfunc sq_inplace_repeat */ \
}; \
static PyMappingMethods Dtool_PyMappingMethods_##CLASS_NAME ={\
static PyMappingMethods Dtool_PyMappingMethods_##CLASS_NAME = \
{ \
0,/*inquiry mp_length */ \
0,/*binaryfunc mp_subscript */ \
0,/*objobjargproc mp_ass_subscript */ \
}; \
EXPORT_THIS Dtool_PyTypedObject Dtool_##CLASS_NAME = {\
EXPORT_THIS Dtool_PyTypedObject Dtool_##CLASS_NAME = \
{ \
{ \
PyObject_HEAD_INIT(NULL) \
0, \
"lib" #MODULE_NAME "." #PUBLIC_NAME, /*type name with module */ \
sizeof(Dtool_PyInstDef), /* tp_basicsize */ \
0, /* tp_itemsize */ \
&Dtool_Deallocate_General, /*Dtool_Deallocate_##CLASS_NAME,*/ /*tp_dealloc*/\
&Dtool_Deallocate_General, /* tp_dealloc */ \
0, /* tp_print */ \
0, /* tp_getattr */ \
0, /* tp_setattr */ \
@ -538,6 +542,16 @@ EXPCL_DTOOLCONFIG int DTOOL_PyObject_Compare(PyObject *v1, PyObject *v2);
EXPCL_DTOOLCONFIG PyObject *
make_list_for_item(PyObject *self, const char *num_name,
const char *element_name);
EXPCL_DTOOLCONFIG PyObject *
copy_from_make_copy(PyObject *self);
EXPCL_DTOOLCONFIG PyObject *
copy_from_copy_constructor(PyObject *self);
EXPCL_DTOOLCONFIG PyObject *
map_deepcopy_to_copy(PyObject *self, PyObject *args);
EXPCL_DTOOLCONFIG PyObject *
PyLongOrInt_FromUnsignedLong(unsigned long value);

View File

@ -51,6 +51,7 @@ extern "C" {
EXPCL_DTOOLCONFIG int PyGen_Type(...);
EXPCL_DTOOLCONFIG int PyGILState_Ensure(...);
EXPCL_DTOOLCONFIG int PyGILState_Release(...);
EXPCL_DTOOLCONFIG int PyImport_GetModuleDict(...);
EXPCL_DTOOLCONFIG int PyInt_AsLong(...);
EXPCL_DTOOLCONFIG int PyInt_AsSsize_t(...);
EXPCL_DTOOLCONFIG int PyInt_FromLong(...);
@ -74,6 +75,7 @@ extern "C" {
EXPCL_DTOOLCONFIG int PyModule_AddObject(...);
EXPCL_DTOOLCONFIG int PyNumber_Long(...);
EXPCL_DTOOLCONFIG int PyObject_Call(...);
EXPCL_DTOOLCONFIG int PyObject_CallFunction(...);
EXPCL_DTOOLCONFIG int PyObject_CallMethod(...);
EXPCL_DTOOLCONFIG int PyObject_CallMethodObjArgs(...);
EXPCL_DTOOLCONFIG int PyObject_CallObject(...);
@ -88,6 +90,7 @@ extern "C" {
EXPCL_DTOOLCONFIG int PyObject_Repr(...);
EXPCL_DTOOLCONFIG int PyObject_SetAttrString(...);
EXPCL_DTOOLCONFIG int PyObject_Str(...);
EXPCL_DTOOLCONFIG int PyObject_Type(...);
EXPCL_DTOOLCONFIG int PySequence_Check(...);
EXPCL_DTOOLCONFIG int PySequence_GetItem(...);
EXPCL_DTOOLCONFIG int PySequence_Size(...);
@ -180,6 +183,7 @@ int PyGen_Check(...) { return 0; }
int PyGen_Type(...) { return 0; }
int PyGILState_Ensure(...) { return 0; }
int PyGILState_Release(...) { return 0; }
int PyImport_GetModuleDict(...) { return 0; }
int PyInt_AsLong(...) { return 0; }
int PyInt_AsSsize_t(...) { return 0; }
int PyInt_FromLong(...) { return 0; }
@ -203,6 +207,7 @@ int PyModule_AddIntConstant(...) { return 0; };
int PyModule_AddObject(...) { return 0; };
int PyNumber_Long(...) { return 0; }
int PyObject_Call(...) { return 0; }
int PyObject_CallFunction(...) { return 0; }
int PyObject_CallMethod(...) { return 0; }
int PyObject_CallMethodObjArgs(...) { return 0; }
int PyObject_CallObject(...) { return 0; }
@ -217,6 +222,7 @@ int PyObject_IsTrue(...) { return 0; }
int PyObject_Repr(...) { return 0; }
int PyObject_SetAttrString(...) { return 0; }
int PyObject_Str(...) { return 0; }
int PyObject_Type(...) { return 0; }
int PySequence_Check(...) { return 0; }
int PySequence_GetItem(...) { return 0; }
int PySequence_Size(...) { return 0; }

View File

@ -46,6 +46,33 @@ const FLOATNAME(LMatrix3) FLOATNAME(LMatrix3)::_ly_to_rz_mat =
FLOATNAME(LMatrix3)::_flip_z_mat * FLOATNAME(LMatrix3)::_y_to_z_up_mat;
#ifdef HAVE_PYTHON
////////////////////////////////////////////////////////////////////
// Function: LMatrix3::__reduce__
// Access: Published
// Description: This special Python method is implement to provide
// support for the pickle module.
////////////////////////////////////////////////////////////////////
PyObject *FLOATNAME(LMatrix3)::
__reduce__(PyObject *self) const {
// We should return at least a 2-tuple, (Class, (args)): the
// necessary class object whose constructor we should call
// (e.g. this), and the arguments necessary to reconstruct this
// object.
PyObject *this_class = PyObject_Type(self);
if (this_class == NULL) {
return NULL;
}
PyObject *result = Py_BuildValue("(O(fffffffff))", this_class,
_m.m._00, _m.m._01, _m.m._02,
_m.m._10, _m.m._11, _m.m._12,
_m.m._20, _m.m._21, _m.m._22);
Py_DECREF(this_class);
return result;
}
#endif // HAVE_PYTHON
////////////////////////////////////////////////////////////////////
// Function: LMatrix::set_scale_shear_mat
// Access: Public

View File

@ -65,6 +65,10 @@ PUBLISHED:
FLOATTYPE e20, FLOATTYPE e21, FLOATTYPE e22);
ALLOC_DELETED_CHAIN(FLOATNAME(LMatrix3));
#ifdef HAVE_PYTHON
PyObject *__reduce__(PyObject *self) const;
#endif
void fill(FLOATTYPE fill_value);
INLINE_LINMATH void set(
FLOATTYPE e00, FLOATTYPE e01, FLOATTYPE e02,

View File

@ -62,6 +62,35 @@ const FLOATNAME(LMatrix4) FLOATNAME(LMatrix4)::_lz_to_ry_mat =
const FLOATNAME(LMatrix4) FLOATNAME(LMatrix4)::_ly_to_rz_mat =
FLOATNAME(LMatrix4)::_flip_z_mat * FLOATNAME(LMatrix4)::_y_to_z_up_mat;
#ifdef HAVE_PYTHON
////////////////////////////////////////////////////////////////////
// Function: LMatrix4::__reduce__
// Access: Published
// Description: This special Python method is implement to provide
// support for the pickle module.
////////////////////////////////////////////////////////////////////
PyObject *FLOATNAME(LMatrix4)::
__reduce__(PyObject *self) const {
// We should return at least a 2-tuple, (Class, (args)): the
// necessary class object whose constructor we should call
// (e.g. this), and the arguments necessary to reconstruct this
// object.
PyObject *this_class = PyObject_Type(self);
if (this_class == NULL) {
return NULL;
}
PyObject *result = Py_BuildValue("(O(ffffffffffffffff))", this_class,
_m.m._00, _m.m._01, _m.m._02, _m.m._03,
_m.m._10, _m.m._11, _m.m._12, _m.m._13,
_m.m._20, _m.m._21, _m.m._22, _m.m._23,
_m.m._30, _m.m._31, _m.m._32, _m.m._33);
Py_DECREF(this_class);
return result;
}
#endif // HAVE_PYTHON
////////////////////////////////////////////////////////////////////
// Function: LMatrix::convert_mat
// Access: Public, Static

View File

@ -60,6 +60,10 @@ PUBLISHED:
FLOATTYPE e30, FLOATTYPE e31, FLOATTYPE e32, FLOATTYPE e33);
ALLOC_DELETED_CHAIN(FLOATNAME(LMatrix4));
#ifdef HAVE_PYTHON
PyObject *__reduce__(PyObject *self) const;
#endif
// Construct a 4x4 matrix given a 3x3 rotation matrix and an optional
// translation component.
INLINE_LINMATH FLOATNAME(LMatrix4)(const FLOATNAME(LMatrix3) &upper3);

View File

@ -21,6 +21,31 @@ const FLOATNAME(LVecBase2) FLOATNAME(LVecBase2)::_unit_x =
const FLOATNAME(LVecBase2) FLOATNAME(LVecBase2)::_unit_y =
FLOATNAME(LVecBase2)(0.0f, 1.0f);
#ifdef HAVE_PYTHON
////////////////////////////////////////////////////////////////////
// Function: LVecBase2::__reduce__
// Access: Published
// Description: This special Python method is implement to provide
// support for the pickle module.
////////////////////////////////////////////////////////////////////
PyObject *FLOATNAME(LVecBase2)::
__reduce__(PyObject *self) const {
// We should return at least a 2-tuple, (Class, (args)): the
// necessary class object whose constructor we should call
// (e.g. this), and the arguments necessary to reconstruct this
// object.
PyObject *this_class = PyObject_Type(self);
if (this_class == NULL) {
return NULL;
}
PyObject *result = Py_BuildValue("(O(ff))", this_class,
(*this)[0], (*this)[1]);
Py_DECREF(this_class);
return result;
}
#endif // HAVE_PYTHON
////////////////////////////////////////////////////////////////////
// Function: LVecBase2::init_type
// Access: Public, Static

View File

@ -39,6 +39,10 @@ PUBLISHED:
INLINE_LINMATH ~FLOATNAME(LVecBase2)();
#ifdef HAVE_PYTHON
PyObject *__reduce__(PyObject *self) const;
#endif
INLINE_LINMATH FLOATTYPE operator [](int i) const;
INLINE_LINMATH FLOATTYPE &operator [](int i);
#ifdef HAVE_PYTHON

View File

@ -12,6 +12,7 @@
//
////////////////////////////////////////////////////////////////////
TypeHandle FLOATNAME(LVecBase3)::_type_handle;
const FLOATNAME(LVecBase3) FLOATNAME(LVecBase3)::_zero =
@ -23,6 +24,32 @@ const FLOATNAME(LVecBase3) FLOATNAME(LVecBase3)::_unit_y =
const FLOATNAME(LVecBase3) FLOATNAME(LVecBase3)::_unit_z =
FLOATNAME(LVecBase3)(0.0f, 0.0f, 1.0f);
#ifdef HAVE_PYTHON
////////////////////////////////////////////////////////////////////
// Function: LVecBase3::__reduce__
// Access: Published
// Description: This special Python method is implement to provide
// support for the pickle module.
////////////////////////////////////////////////////////////////////
PyObject *FLOATNAME(LVecBase3)::
__reduce__(PyObject *self) const {
// We should return at least a 2-tuple, (Class, (args)): the
// necessary class object whose constructor we should call
// (e.g. this), and the arguments necessary to reconstruct this
// object.
PyObject *this_class = PyObject_Type(self);
if (this_class == NULL) {
return NULL;
}
PyObject *result = Py_BuildValue("(O(fff))", this_class,
(*this)[0], (*this)[1], (*this)[2]);
Py_DECREF(this_class);
return result;
}
#endif // HAVE_PYTHON
////////////////////////////////////////////////////////////////////
// Function: LVecBase3::init_type
// Access: Public, Static

View File

@ -37,6 +37,10 @@ PUBLISHED:
INLINE_LINMATH ~FLOATNAME(LVecBase3)();
#ifdef HAVE_PYTHON
PyObject *__reduce__(PyObject *self) const;
#endif
INLINE_LINMATH FLOATTYPE operator [](int i) const;
INLINE_LINMATH FLOATTYPE &operator [](int i);
#ifdef HAVE_PYTHON

View File

@ -25,6 +25,31 @@ const FLOATNAME(LVecBase4) FLOATNAME(LVecBase4)::_unit_z =
const FLOATNAME(LVecBase4) FLOATNAME(LVecBase4)::_unit_w =
FLOATNAME(LVecBase4)(0.0f, 0.0f, 0.0f, 1.0f);
#ifdef HAVE_PYTHON
////////////////////////////////////////////////////////////////////
// Function: LVecBase4::__reduce__
// Access: Published
// Description: This special Python method is implement to provide
// support for the pickle module.
////////////////////////////////////////////////////////////////////
PyObject *FLOATNAME(LVecBase4)::
__reduce__(PyObject *self) const {
// We should return at least a 2-tuple, (Class, (args)): the
// necessary class object whose constructor we should call
// (e.g. this), and the arguments necessary to reconstruct this
// object.
PyObject *this_class = PyObject_Type(self);
if (this_class == NULL) {
return NULL;
}
PyObject *result = Py_BuildValue("(O(ffff))", this_class,
(*this)[0], (*this)[1], (*this)[2], (*this)[3]);
Py_DECREF(this_class);
return result;
}
#endif // HAVE_PYTHON
////////////////////////////////////////////////////////////////////
// Function: LVecBase4::init_type
// Access: Public, Static

View File

@ -38,6 +38,10 @@ PUBLISHED:
INLINE_LINMATH ~FLOATNAME(LVecBase4)();
#ifdef HAVE_PYTHON
PyObject *__reduce__(PyObject *self) const;
#endif
INLINE_LINMATH FLOATTYPE operator [](int i) const;
INLINE_LINMATH FLOATTYPE &operator [](int i);
#ifdef HAVE_PYTHON

View File

@ -66,6 +66,7 @@
#include "pStatCollector.h"
#include "pStatTimer.h"
#include "modelNode.h"
#include "py_panda.h"
// stack seems to overflow on Intel C++ at 7000. If we need more than
// 7000, need to increase stack size.
@ -127,6 +128,128 @@ static ConfigVariableEnum<EmptyNodePathType> empty_node_path
// ***End temporary transition code for operator bool
#ifdef HAVE_PYTHON
////////////////////////////////////////////////////////////////////
// Function: NodePath::__copy__
// Access: Published
// Description: A special Python method that is invoked by
// copy.copy(node). Unlike the NodePath copy
// constructor, this makes a duplicate copy of the
// underlying PandaNode (but shares children, instead of
// copying them or omitting them).
////////////////////////////////////////////////////////////////////
NodePath NodePath::
__copy__() const {
if (is_empty()) {
// Invoke the copy constructor if we have no node.
return *this;
}
// If we do have a node, duplicate it, and wrap it in a new
// NodePath.
return NodePath(node()->__copy__());
}
#endif // HAVE_PYTHON
#ifdef HAVE_PYTHON
////////////////////////////////////////////////////////////////////
// Function: NodePath::__deepcopy__
// Access: Published
// Description: A special Python method that is invoked by
// copy.deepcopy(np). This calls copy_to() unless the
// NodePath is already present in the provided
// dictionary.
////////////////////////////////////////////////////////////////////
PyObject *NodePath::
__deepcopy__(PyObject *self, PyObject *memo) const {
IMPORT_THIS struct Dtool_PyTypedObject Dtool_NodePath;
// Borrowed reference.
PyObject *dupe = PyDict_GetItem(memo, self);
if (dupe != NULL) {
// Already in the memo dictionary.
Py_INCREF(dupe);
return dupe;
}
NodePath *np_dupe;
if (is_empty()) {
np_dupe = new NodePath(*this);
} else {
np_dupe = new NodePath(copy_to(NodePath()));
}
dupe = DTool_CreatePyInstance((void *)np_dupe, Dtool_NodePath,
true, false);
if (PyDict_SetItem(memo, self, dupe) != 0) {
Py_DECREF(dupe);
return NULL;
}
return dupe;
}
#endif // HAVE_PYTHON
#ifdef HAVE_PYTHON
////////////////////////////////////////////////////////////////////
// Function: NodePath::__reduce__
// Access: Published
// Description: This special Python method is implement to provide
// support for the pickle module.
////////////////////////////////////////////////////////////////////
PyObject *NodePath::
__reduce__(PyObject *self) const {
// We should return at least a 2-tuple, (Class, (args)): the
// necessary class object whose constructor we should call
// (e.g. this), and the arguments necessary to reconstruct this
// object.
if (is_empty()) {
// Reconstruct an empty NodePath. Not a 100% reconstruction,
// because we lose the specific error status, but I don't think
// that matters much.
PyObject *this_class = PyObject_Type(self);
if (this_class == NULL) {
return NULL;
}
PyObject *result = Py_BuildValue("(O())", this_class);
Py_DECREF(this_class);
return result;
}
// We have a non-empty NodePath. We need to streamify the
// underlying node.
string bam_stream;
if (!node()->encode_to_bam_stream(bam_stream)) {
ostringstream stream;
stream << "Could not bamify object of type " << node()->get_type() << "\n";
string message = stream.str();
PyErr_SetString(PyExc_TypeError, message.c_str());
return NULL;
}
// Start by getting this class object.
PyObject *this_class = PyObject_Type(self);
if (this_class == NULL) {
return NULL;
}
PyObject *func = TypedWritable::find_global_decode(this_class, "pyDecodeNodePathFromBamStream");
if (func == NULL) {
PyErr_SetString(PyExc_TypeError, "Couldn't find pyDecodeNodePathFromBamStream()");
Py_DECREF(this_class);
return NULL;
}
Py_DECREF(this_class);
PyObject *result = Py_BuildValue("(O(s#))", func, bam_stream.data(), bam_stream.size());
Py_DECREF(func);
return result;
}
#endif // HAVE_PYTHON
////////////////////////////////////////////////////////////////////
// Function: NodePath::operator bool
// Access: Published
@ -7292,3 +7415,23 @@ r_find_all_materials(PandaNode *node, const RenderState *state,
r_find_all_materials(child, next_state, materials);
}
}
#ifdef HAVE_PYTHON
////////////////////////////////////////////////////////////////////
// Function: py_decode_NodePath_from_bam_stream
// Access: Published
// Description: This wrapper is defined as a global function to suit
// pickle's needs.
////////////////////////////////////////////////////////////////////
NodePath
py_decode_NodePath_from_bam_stream(const string &data) {
PT(PandaNode) node = PandaNode::decode_from_bam_stream(data);
if (node == (PandaNode *)NULL) {
PyErr_SetString(PyExc_ValueError, "Could not unpack bam stream");
return NodePath();
}
return NodePath(node);
}
#endif // HAVE_PYTHON

View File

@ -168,6 +168,12 @@ PUBLISHED:
INLINE NodePath(const NodePath &copy);
INLINE void operator = (const NodePath &copy);
#ifdef HAVE_PYTHON
NodePath __copy__() const;
PyObject *__deepcopy__(PyObject *self, PyObject *memo) const;
PyObject *__reduce__(PyObject *self) const;
#endif
INLINE static NodePath not_found();
INLINE static NodePath removed();
INLINE static NodePath fail();
@ -949,6 +955,12 @@ private:
INLINE ostream &operator << (ostream &out, const NodePath &node_path);
#ifdef HAVE_PYTHON
BEGIN_PUBLISH
NodePath py_decode_NodePath_from_bam_stream(const string &data);
END_PUBLISH
#endif
#include "nodePath.I"
#endif

View File

@ -28,6 +28,7 @@
#include "config_mathutil.h"
#include "lightReMutexHolder.h"
#include "graphicsStateGuardianBase.h"
#include "py_panda.h"
// This category is just temporary for debugging convenience.
NotifyCategoryDecl(drawmask, EXPCL_PANDA_PGRAPH, EXPTP_PANDA_PGRAPH);
@ -211,20 +212,6 @@ as_reference_count() {
return this;
}
////////////////////////////////////////////////////////////////////
// Function: PandaNode::make_copy
// Access: Public, Virtual
// Description: Returns a newly-allocated PandaNode that is a shallow
// copy of this one. It will be a different pointer,
// but its internal data may or may not be shared with
// that of the original PandaNode. No children will be
// copied.
////////////////////////////////////////////////////////////////////
PandaNode *PandaNode::
make_copy() const {
return new PandaNode(*this);
}
////////////////////////////////////////////////////////////////////
// Function: PandaNode::dupe_for_flatten
// Access: Public, Virtual
@ -608,6 +595,20 @@ void PandaNode::
add_for_draw(CullTraverser *, CullTraverserData &) {
}
////////////////////////////////////////////////////////////////////
// Function: PandaNode::make_copy
// Access: Published, Virtual
// Description: Returns a newly-allocated PandaNode that is a shallow
// copy of this one. It will be a different pointer,
// but its internal data may or may not be shared with
// that of the original PandaNode. No children will be
// copied.
////////////////////////////////////////////////////////////////////
PandaNode *PandaNode::
make_copy() const {
return new PandaNode(*this);
}
////////////////////////////////////////////////////////////////////
// Function: PandaNode::copy_subgraph
// Access: Published
@ -618,12 +619,79 @@ add_for_draw(CullTraverser *, CullTraverserData &) {
// will impede normal use of the PandaNode.
////////////////////////////////////////////////////////////////////
PT(PandaNode) PandaNode::
copy_subgraph(Thread *current_thread) const
{
copy_subgraph(Thread *current_thread) const {
InstanceMap inst_map;
return r_copy_subgraph(inst_map, current_thread);
}
#ifdef HAVE_PYTHON
////////////////////////////////////////////////////////////////////
// Function: PandaNode::__copy__
// Access: Published
// Description: A special Python method that is invoked by
// copy.copy(node). Unlike the PandaNode copy
// constructor, which creates a new node without
// children, this shares child pointers (essentially
// making every child an instance). This is intended to
// simulate the behavior of copy.copy() for other
// objects.
////////////////////////////////////////////////////////////////////
PT(PandaNode) PandaNode::
__copy__() const {
Thread *current_thread = Thread::get_current_thread();
PT(PandaNode) node_dupe = make_copy();
Children children = get_children(current_thread);
int num_children = children.get_num_children();
for (int i = 0; i < num_children; ++i) {
PandaNode *child = children.get_child(i);
node_dupe->add_child(children.get_child(i), children.get_child_sort(i));
}
return node_dupe;
}
#endif // HAVE_PYTHON
#ifdef HAVE_PYTHON
////////////////////////////////////////////////////////////////////
// Function: PandaNode::__deepcopy__
// Access: Published
// Description: A special Python method that is invoked by
// copy.deepcopy(node). This calls copy_subgraph()
// unless the node is already present in the provided
// dictionary.
////////////////////////////////////////////////////////////////////
PyObject *PandaNode::
__deepcopy__(PyObject *self, PyObject *memo) const {
IMPORT_THIS struct Dtool_PyTypedObject Dtool_PandaNode;
// Borrowed reference.
PyObject *dupe = PyDict_GetItem(memo, self);
if (dupe != NULL) {
// Already in the memo dictionary.
Py_INCREF(dupe);
return dupe;
}
PT(PandaNode) node_dupe = copy_subgraph();
// DTool_CreatePyInstanceTyped() steals a C++ reference.
node_dupe->ref();
dupe = DTool_CreatePyInstanceTyped
((void *)node_dupe.p(), Dtool_PandaNode, true, false,
node_dupe->get_type_index());
if (PyDict_SetItem(memo, self, dupe) != 0) {
Py_DECREF(dupe);
return NULL;
}
return dupe;
}
#endif // HAVE_PYTHON
////////////////////////////////////////////////////////////////////
// Function: PandaNode::count_num_descendants
// Access: Published
@ -2597,6 +2665,32 @@ is_ambient_light() const {
return false;
}
////////////////////////////////////////////////////////////////////
// Function: PandaNode::decode_from_bam_stream
// Access: Published, Static
// Description: Reads the string created by a previous call to
// encode_to_bam_stream(), and extracts and returns the
// single object on that string. Returns NULL on error.
//
// This method is intended to replace
// decode_raw_from_bam_stream() when you know the stream
// in question returns an object of type PandaNode,
// allowing for easier reference count management. Note
// that the caller is still responsible for maintaining
// the reference count on the return value.
////////////////////////////////////////////////////////////////////
PT(PandaNode) PandaNode::
decode_from_bam_stream(const string &data) {
TypedWritable *object;
ReferenceCount *ref_ptr;
if (!TypedWritable::decode_raw_from_bam_stream(object, ref_ptr, data)) {
return NULL;
}
return DCAST(PandaNode, object);
}
////////////////////////////////////////////////////////////////////
// Function: PandaNode::get_internal_bounds
// Access: Protected

View File

@ -84,7 +84,6 @@ private:
public:
virtual ReferenceCount *as_reference_count();
virtual PandaNode *make_copy() const;
virtual PandaNode *dupe_for_flatten() const;
virtual bool safe_to_flatten() const;
@ -116,8 +115,14 @@ public:
virtual void add_for_draw(CullTraverser *trav, CullTraverserData &data);
PUBLISHED:
virtual PandaNode *make_copy() const;
PT(PandaNode) copy_subgraph(Thread *current_thread = Thread::get_current_thread()) const;
#ifdef HAVE_PYTHON
PT(PandaNode) __copy__() const;
PyObject *__deepcopy__(PyObject *self, PyObject *memo) const;
#endif
INLINE int get_num_parents(Thread *current_thread = Thread::get_current_thread()) const;
INLINE PandaNode *get_parent(int n, Thread *current_thread = Thread::get_current_thread()) const;
INLINE int find_parent(PandaNode *node, Thread *current_thread = Thread::get_current_thread()) const;
@ -300,6 +305,10 @@ PUBLISHED:
};
INLINE int get_fancy_bits(Thread *current_thread = Thread::get_current_thread()) const;
PUBLISHED:
static PT(PandaNode) decode_from_bam_stream(const string &data);
protected:
class BoundsData;

View File

@ -66,3 +66,28 @@ INLINE UpdateSeq TypedWritable::
get_bam_modified() const {
return _bam_modified;
}
////////////////////////////////////////////////////////////////////
// Function: TypedWritable::encode_to_bam_stream
// Access: Published
// Description: Converts the TypedWritable object into a single
// stream of data using a BamWriter, and returns that
// data as a string string. Returns empty string on
// failure.
//
// This is a convenience method particularly useful for
// cases when you are only serializing a single object.
// If you have many objects to process, it is more
// efficient to use the same BamWriter to serialize all
// of them together.
////////////////////////////////////////////////////////////////////
string TypedWritable::
encode_to_bam_stream() const {
string data;
if (!encode_to_bam_stream(data)) {
return string();
}
return data;
}

View File

@ -14,7 +14,11 @@
#include "typedWritable.h"
#include "bamWriter.h"
#include "bamReader.h"
#include "datagramOutputFile.h"
#include "datagramInputFile.h"
#include "lightMutexHolder.h"
#include "bam.h"
LightMutex TypedWritable::_bam_writers_lock;
@ -144,3 +148,276 @@ ReferenceCount *TypedWritable::
as_reference_count() {
return NULL;
}
#ifdef HAVE_PYTHON
////////////////////////////////////////////////////////////////////
// Function: TypedWritable::__reduce__
// Access: Published
// Description: This special Python method is implement to provide
// support for the pickle module.
////////////////////////////////////////////////////////////////////
PyObject *TypedWritable::
__reduce__(PyObject *self) const {
// We should return at least a 2-tuple, (Class, (args)): the
// necessary class object whose constructor we should call
// (e.g. this), and the arguments necessary to reconstruct this
// object.
// Check that we have a decodeFromBamStream python method. If not,
// we can't use this interface.
PyObject *method = PyObject_GetAttrString(self, "decodeFromBamStream");
if (method == NULL) {
ostringstream stream;
stream << "Cannot pickle objects of type " << get_type() << "\n";
string message = stream.str();
PyErr_SetString(PyExc_TypeError, message.c_str());
return NULL;
}
Py_DECREF(method);
// First, streamify the object, if possible.
string bam_stream;
if (!encode_to_bam_stream(bam_stream)) {
ostringstream stream;
stream << "Could not bamify object of type " << get_type() << "\n";
string message = stream.str();
PyErr_SetString(PyExc_TypeError, message.c_str());
return NULL;
}
// Start by getting this class object.
PyObject *this_class = PyObject_Type(self);
if (this_class == NULL) {
return NULL;
}
PyObject *func = find_global_decode(this_class, "pyDecodeTypedWritableFromBamStream");
if (func == NULL) {
PyErr_SetString(PyExc_TypeError, "Couldn't find pyDecodeTypedWritableFromBamStream()");
Py_DECREF(this_class);
return NULL;
}
PyObject *result = Py_BuildValue("(O(Os#))", func, this_class, bam_stream.data(), bam_stream.size());
Py_DECREF(func);
Py_DECREF(this_class);
return result;
}
#endif // HAVE_PYTHON
////////////////////////////////////////////////////////////////////
// Function: TypedWritable::encode_to_bam_stream
// Access: Published
// Description: Converts the TypedWritable object into a single
// stream of data using a BamWriter, and stores that
// data in the indicated string. Returns true on
// success, false on failure.
//
// This is a convenience method particularly useful for
// cases when you are only serializing a single object.
// If you have many objects to process, it is more
// efficient to use the same BamWriter to serialize all
// of them together.
////////////////////////////////////////////////////////////////////
bool TypedWritable::
encode_to_bam_stream(string &data) const {
data.clear();
ostringstream stream;
// We use nested scoping to ensure the destructors get called in the
// right order.
{
DatagramOutputFile dout;
if (!dout.open(stream)) {
return false;
}
if (!dout.write_header(_bam_header)) {
return false;
}
{
BamWriter writer(&dout, "bam_stream");
if (!writer.init()) {
return false;
}
if (!writer.write_object(this)) {
return false;
}
}
}
data = stream.str();
return true;
}
////////////////////////////////////////////////////////////////////
// Function: TypedWritable::decode_raw_from_bam_stream
// Access: Published, Static
// Description: Reads the string created by a previous call to
// encode_to_bam_stream(), and extracts the single
// object on that string. Returns true on success,
// false on on error.
//
// This variant sets the TypedWritable and
// ReferenceCount pointers separately; both are pointers
// to the same object. The reference count is not
// incremented; it is the caller's responsibility to
// manage the reference count.
//
// Note that this method cannot be used to retrieve
// objects that do not inherit from ReferenceCount,
// because these objects cannot persist beyond the
// lifetime of the BamReader that reads them. To
// retrieve these objects from a bam stream, you must
// construct a BamReader directly.
//
// If you happen to know that the particular object in
// question inherits from TypedWritableReferenceCount or
// PandaNode, consider calling the variant of
// decode_from_bam_stream() defined for those methods,
// which presents a simpler interface.
////////////////////////////////////////////////////////////////////
bool TypedWritable::
decode_raw_from_bam_stream(TypedWritable *&ptr, ReferenceCount *&ref_ptr,
const string &data) {
istringstream stream(data);
DatagramInputFile din;
if (!din.open(stream)) {
return false;
}
string head;
if (!din.read_header(head, _bam_header.size())) {
return false;
}
if (head != _bam_header) {
return false;
}
// We scope this so we can control when the BamReader destructs.
{
BamReader reader(&din, "bam_stream");
if (!reader.init()) {
return false;
}
if (!reader.read_object(ptr, ref_ptr)) {
return false;
}
if (!reader.resolve()) {
return false;
}
if (ref_ptr == NULL) {
// Can't support non-reference-counted objects.
return false;
}
// Protect the pointer from accidental deletion when the BamReader
// goes away.
ref_ptr->ref();
}
// Now decrement the ref count, without deleting the object. This
// may reduce the reference count to zero, but that's OK--we trust
// the caller to manage the reference count from this point on.
ref_ptr->unref();
return true;
}
#ifdef HAVE_PYTHON
////////////////////////////////////////////////////////////////////
// Function: TypedWritable::find_global_decode
// Access: Public, Static
// Description: This is a support function for __reduce__(). It
// searches for the global function
// pyDecodeTypedWritableFromBamStream() in this class's
// module, or in the module for any base class. (It's
// really looking for the libpanda module, but we can't
// be sure what name that module was loaded under, so we
// search upwards this way.)
//
// Returns: new reference on success, or NULL on failure.
////////////////////////////////////////////////////////////////////
PyObject *TypedWritable::
find_global_decode(PyObject *this_class, const char *func_name) {
PyObject *module_name = PyObject_GetAttrString(this_class, "__module__");
if (module_name != NULL) {
// borrowed reference
PyObject *sys_modules = PyImport_GetModuleDict();
if (sys_modules != NULL) {
// borrowed reference
PyObject *module = PyDict_GetItem(sys_modules, module_name);
if (module != NULL){
PyObject *func = PyObject_GetAttrString(module, func_name);
if (func != NULL) {
Py_DECREF(module_name);
return func;
}
}
}
}
Py_DECREF(module_name);
PyObject *bases = PyObject_GetAttrString(this_class, "__bases__");
if (bases != NULL) {
if (PySequence_Check(bases)) {
Py_ssize_t size = PySequence_Size(bases);
for (Py_ssize_t i = 0; i < size; ++i) {
PyObject *base = PySequence_GetItem(bases, i);
if (base != NULL) {
PyObject *func = find_global_decode(base, func_name);
Py_DECREF(base);
if (func != NULL) {
Py_DECREF(bases);
return func;
}
}
}
}
Py_DECREF(bases);
}
return NULL;
}
#endif // HAVE_PYTHON
#ifdef HAVE_PYTHON
////////////////////////////////////////////////////////////////////
// Function: py_decode_TypedWritable_from_bam_stream
// Access: Published
// Description: This wrapper is defined as a global function to suit
// pickle's needs.
////////////////////////////////////////////////////////////////////
PyObject *
py_decode_TypedWritable_from_bam_stream(PyObject *this_class, const string &data) {
// We need the function TypedWritable::decode_from_bam_stream, which
// invokes the BamReader to reconstruct this object. Since we use
// the specific object's class as the pointer, we get the particular
// instance of decode_from_bam_stream appropriate to this class.
PyObject *func = PyObject_GetAttrString(this_class, "decodeFromBamStream");
if (func == NULL) {
return NULL;
}
PyObject *result = PyObject_CallFunction(func, (char *)"(s#)", data.data(), data.size());
if (result == NULL) {
return NULL;
}
if (result == Py_None) {
Py_DECREF(result);
PyErr_SetString(PyExc_ValueError, "Could not unpack bam stream");
return NULL;
}
return result;
}
#endif // HAVE_PYTHON

View File

@ -60,6 +60,21 @@ PUBLISHED:
INLINE void mark_bam_modified();
INLINE UpdateSeq get_bam_modified() const;
#ifdef HAVE_PYTHON
PyObject *__reduce__(PyObject *self) const;
#endif
INLINE string encode_to_bam_stream() const;
bool encode_to_bam_stream(string &data) const;
static bool decode_raw_from_bam_stream(TypedWritable *&ptr,
ReferenceCount *&ref_ptr,
const string &data);
public:
#ifdef HAVE_PYTHON
static PyObject *find_global_decode(PyObject *this_class, const char *func_name);
#endif
private:
// We may need to store a list of the BamWriter(s) that have a
// reference to this object, so that we can remove the object from
@ -70,12 +85,10 @@ private:
UpdateSeq _bam_modified;
PUBLISHED:
public:
static TypeHandle get_class_type() {
return _type_handle;
}
public:
static void init_type() {
TypedObject::init_type();
register_type(_type_handle, "TypedWritable",
@ -96,6 +109,12 @@ private:
friend class BamWriter;
};
#ifdef HAVE_PYTHON
BEGIN_PUBLISH
PyObject *py_decode_TypedWritable_from_bam_stream(PyObject *this_class, const string &data);
END_PUBLISH
#endif
#include "typedWritable.I"
#endif

View File

@ -26,3 +26,30 @@ ReferenceCount *TypedWritableReferenceCount::
as_reference_count() {
return this;
}
////////////////////////////////////////////////////////////////////
// Function: TypedWritableReferenceCount::decode_from_bam_stream
// Access: Published, Static
// Description: Reads the string created by a previous call to
// encode_to_bam_stream(), and extracts and returns the
// single object on that string. Returns NULL on error.
//
// This method is intended to replace
// decode_raw_from_bam_stream() when you know the stream
// in question returns an object of type
// TypedWritableReferenceCount, allowing for easier
// reference count management. Note that the caller is
// still responsible for maintaining the reference count
// on the return value.
////////////////////////////////////////////////////////////////////
PT(TypedWritableReferenceCount) TypedWritableReferenceCount::
decode_from_bam_stream(const string &data) {
TypedWritable *object;
ReferenceCount *ref_ptr;
if (!TypedWritable::decode_raw_from_bam_stream(object, ref_ptr, data)) {
return NULL;
}
return DCAST(TypedWritableReferenceCount, object);
}

View File

@ -40,18 +40,17 @@ public:
virtual ReferenceCount *as_reference_count();
PUBLISHED:
static PT(TypedWritableReferenceCount) decode_from_bam_stream(const string &data);
public:
virtual TypeHandle get_type() const {
return get_class_type();
}
virtual TypeHandle force_init_type() {init_type(); return get_class_type();}
PUBLISHED:
static TypeHandle get_class_type() {
return _type_handle;
}
public:
static void init_type() {
TypedWritable::init_type();
ReferenceCount::init_type();