Browse Source

read parameters from concrete classes

David Rose 21 years ago
parent
commit
19e98401f1

+ 30 - 2
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;
   }

+ 1 - 1
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,

+ 17 - 17
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

+ 84 - 9
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();
+  } 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);
-    for (int i = 0; i < size; i++) {
-      PyObject *element = PySequence_GetItem(object, i);
-      pack_object(element);
-      Py_DECREF(element);
+
+    // 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

+ 1 - 0
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