From 19e98401f1f174a0f5faf61a5ce1539428c99457 Mon Sep 17 00:00:00 2001 From: David Rose Date: Mon, 28 Jun 2004 22:58:15 +0000 Subject: [PATCH] read parameters from concrete classes --- direct/src/dcparser/dcClass.cxx | 32 ++++++++++- direct/src/dcparser/dcClass.h | 2 +- direct/src/dcparser/dcField.cxx | 34 ++++++------ direct/src/dcparser/dcPacker.cxx | 95 ++++++++++++++++++++++++++++---- direct/src/dcparser/dcPacker.h | 1 + 5 files changed, 134 insertions(+), 30 deletions(-) diff --git a/direct/src/dcparser/dcClass.cxx b/direct/src/dcparser/dcClass.cxx index 6681fab54a..0d6fa6deac 100644 --- a/direct/src/dcparser/dcClass.cxx +++ b/direct/src/dcparser/dcClass.cxx @@ -423,8 +423,34 @@ direct_update(PyObject *distobj, const string &field_name, // Returns true on success, false on failure. //////////////////////////////////////////////////////////////////// bool DCClass:: -pack_required_field(DCPacker &packer, PyObject *distobj, DCField *field) const { - DCAtomicField *atom = field->as_atomic_field(); +pack_required_field(DCPacker &packer, PyObject *distobj, + const DCField *field) const { + const DCParameter *parameter = ((DCField *)field)->as_parameter(); + if (parameter != (DCParameter *)NULL) { + // This is the easy case: to pack a parameter, we just look on the + // class object for the data element. + string field_name = field->get_name(); + + if (!PyObject_HasAttrString(distobj, (char *)field_name.c_str())) { + ostringstream strm; + strm << "Data element " << field_name + << ", required by dc file for dclass " << get_name() + << ", not defined on object."; + nassert_raise(strm.str()); + return false; + } + PyObject *result = + PyObject_GetAttrString(distobj, (char *)field_name.c_str()); + nassertr(result != (PyObject *)NULL, false); + + // Now pack the value into the datagram. + bool pack_ok = parameter->pack_args(packer, result); + Py_DECREF(result); + + return pack_ok; + } + + const DCAtomicField *atom = ((DCField *)field)->as_atomic_field(); if (atom == (DCAtomicField *)NULL) { ostringstream strm; strm << "Cannot pack non-atomic field " << field->get_name() @@ -476,6 +502,8 @@ pack_required_field(DCPacker &packer, PyObject *distobj, DCField *field) const { Py_DECREF(empty_args); Py_DECREF(func); if (result == (PyObject *)NULL) { + // We don't set this as an exception, since presumably the Python + // method itself has already triggered a Python exception. cerr << "Error when calling " << get_name << "\n"; return false; } diff --git a/direct/src/dcparser/dcClass.h b/direct/src/dcparser/dcClass.h index 368b06cd7f..d097d6dc6a 100644 --- a/direct/src/dcparser/dcClass.h +++ b/direct/src/dcparser/dcClass.h @@ -69,7 +69,7 @@ PUBLISHED: void direct_update(PyObject *distobj, const string &field_name, const Datagram &datagram); bool pack_required_field(DCPacker &packer, PyObject *distobj, - DCField *field) const; + const DCField *field) const; Datagram client_format_update(const string &field_name, diff --git a/direct/src/dcparser/dcField.cxx b/direct/src/dcparser/dcField.cxx index 4ad82d6c67..c0fa3db991 100644 --- a/direct/src/dcparser/dcField.cxx +++ b/direct/src/dcparser/dcField.cxx @@ -195,8 +195,6 @@ pack_args(DCPacker &packer, PyObject *sequence) const { nassertr(!packer.had_error(), false); nassertr(packer.get_current_field() == this, false); - nassertr(PySequence_Check(sequence), false); - packer.pack_object(sequence); if (!packer.had_error()) { /* @@ -208,22 +206,24 @@ pack_args(DCPacker &packer, PyObject *sequence) const { return true; } - PyObject *tuple = PySequence_Tuple(sequence); - PyObject *str = PyObject_Str(tuple); - - ostringstream strm; - if (packer.had_pack_error()) { - strm << "Incorrect arguments to field: " << get_name() - << PyString_AsString(str); - } else { - strm << "Value out of range on field: " << get_name() - << PyString_AsString(str); - } - - Py_DECREF(str); - Py_DECREF(tuple); + if (!Notify::ptr()->has_assert_failed()) { + PyObject *tuple = PySequence_Tuple(sequence); + PyObject *str = PyObject_Str(tuple); - nassert_raise(strm.str()); + ostringstream strm; + if (packer.had_pack_error()) { + strm << "Incorrect arguments to field: " << get_name() + << PyString_AsString(str); + } else { + strm << "Value out of range on field: " << get_name() + << PyString_AsString(str); + } + + Py_DECREF(str); + Py_DECREF(tuple); + + nassert_raise(strm.str()); + } return false; } #endif // HAVE_PYTHON diff --git a/direct/src/dcparser/dcPacker.cxx b/direct/src/dcparser/dcPacker.cxx index c6bd84ce63..6dfeb89cbc 100755 --- a/direct/src/dcparser/dcPacker.cxx +++ b/direct/src/dcparser/dcPacker.cxx @@ -634,8 +634,6 @@ unpack_skip() { void DCPacker:: pack_object(PyObject *object) { nassertv(_mode == M_pack || _mode == M_repack); - PyObject *str = PyObject_Str(object); - Py_DECREF(str); if (PyInt_Check(object)) { pack_int(PyInt_AS_LONG(object)); @@ -654,15 +652,65 @@ pack_object(PyObject *object) { pack_string(string(buffer, length)); } - } else if (PySequence_Check(object)) { - push(); - int size = PySequence_Size(object); - for (int i = 0; i < size; i++) { - PyObject *element = PySequence_GetItem(object, i); - pack_object(element); - Py_DECREF(element); + } else { + DCClass *dclass = NULL; + const DCClassParameter *class_param = ((DCPackerInterface *)get_current_field())->as_class_parameter(); + if (class_param != (DCClassParameter *)NULL) { + dclass = class_param->get_class(); + } + + int size = PySequence_Size(object); + + // If dclass is not NULL, the packer is expecting a class object. + // There are then two cases: (1) the user has supplied a matching + // class object, or (2) the user has supplied a sequence object. + // Unfortunately, it may be difficult to differentiate these two + // cases, since a class object may also be a sequence object. + + // The rule to differentiate them is: + + // (1) If the supplied class object is an instance of the expected + // class object, it is considered to be a class object. + + // (2) Otherwise, if the supplied class object has a __len__() + // method (i.e. PySequence_Size() returns a number >= 0), then it + // is considered to be a sequence. + + // (3) Otherwise, it is considered to be a class object. + + if (dclass != (DCClass *)NULL && + ((dclass->get_class_def() != (PyObject *)NULL && + PyObject_IsInstance(object, dclass->get_class_def())) || + size < 0)) { + // The supplied object is either an instance of the expected + // class object, or the size is less than 0--this is case (1) or + // (3). + pack_class_object(dclass, object); + + } else if (size >= 0) { + // The supplied object is not an instance of the expected class + // object, and it does return a valid size. This is case (2). + push(); + int size = PySequence_Size(object); + for (int i = 0; i < size; i++) { + PyObject *element = PySequence_GetItem(object, i); + pack_object(element); + Py_DECREF(element); + } + pop(); + + } else { + // The supplied object does not return a valid size, and we + // weren't expecting a class parameter. This is none of the + // above, an error. + ostringstream strm; + PyObject *str = PyObject_Str(object); + strm << "Don't know how to pack object: " + << PyString_AsString(str) << "\n"; + Py_DECREF(str); + nassert_raise(strm.str()); + _pack_error = true; } - pop(); } } #endif // HAVE_PYTHON @@ -1034,6 +1082,33 @@ clear() { _root = NULL; } +#ifdef HAVE_PYTHON +//////////////////////////////////////////////////////////////////// +// Function: DCPacker::pack_class_object +// Access: Private +// Description: Given that the current element is a ClassParameter +// for a Python class object, try to extract the +// appropriate values from the class object and pack in. +//////////////////////////////////////////////////////////////////// +void DCPacker:: +pack_class_object(DCClass *dclass, PyObject *object) { + PyObject *str = PyObject_Str(object); + cerr << "pack_class_object(" << dclass->get_name() << ", " + << PyString_AsString(str) << ")\n"; + Py_DECREF(str); + push(); + while (more_nested_fields()) { + const DCField *field = ((DCPackerInterface *)get_current_field())->as_field(); + nassertv(field != (DCField *)NULL); + + if (!dclass->pack_required_field(*this, object, field)) { + break; + } + } + pop(); +} +#endif // HAVE_PYTHON + #ifdef HAVE_PYTHON //////////////////////////////////////////////////////////////////// // Function: DCPacker::unpack_class_object diff --git a/direct/src/dcparser/dcPacker.h b/direct/src/dcparser/dcPacker.h index 12198f93b5..bef814f319 100755 --- a/direct/src/dcparser/dcPacker.h +++ b/direct/src/dcparser/dcPacker.h @@ -178,6 +178,7 @@ private: void clear(); #ifdef HAVE_PYTHON + void pack_class_object(DCClass *dclass, PyObject *object); PyObject *unpack_class_object(DCClass *dclass); void set_class_element(PyObject *object, const DCField *field); #endif