Returns true iff there were no "internal" errors. In other words, errors that has nothing to do with structure checking. If an "internal" error occurred, the appropriate Python error will be set and the caller can propage it directly to the user. Both `error_msg` and `is_type_error` must be non-null. `error_msg` must be empty. Leaves `error_msg` empty if structures matched. Else, fills `error_msg
| 653 | // with appropriate error and sets `is_type_error` to true iff |
| 654 | // the error to be raised should be TypeError. |
| 655 | bool AssertSameStructureHelper( |
| 656 | PyObject* o1, PyObject* o2, bool check_types, string* error_msg, |
| 657 | bool* is_type_error, |
| 658 | const std::function<int(PyObject*)>& is_sequence_helper, |
| 659 | const std::function<ValueIteratorPtr(PyObject*)>& value_iterator_getter, |
| 660 | bool check_composite_tensor_type_spec) { |
| 661 | DCHECK(error_msg); |
| 662 | DCHECK(is_type_error); |
| 663 | const bool is_seq1 = is_sequence_helper(o1); |
| 664 | const bool is_seq2 = is_sequence_helper(o2); |
| 665 | if (PyErr_Occurred()) return false; |
| 666 | if (is_seq1 != is_seq2) { |
| 667 | string seq_str = is_seq1 ? PyObjectToString(o1) : PyObjectToString(o2); |
| 668 | string non_seq_str = is_seq1 ? PyObjectToString(o2) : PyObjectToString(o1); |
| 669 | *is_type_error = false; |
| 670 | *error_msg = tensorflow::strings::StrCat( |
| 671 | "Substructure \"", seq_str, "\" is a sequence, while substructure \"", |
| 672 | non_seq_str, "\" is not"); |
| 673 | return true; |
| 674 | } |
| 675 | |
| 676 | // Got to objects that are considered non-sequences. Note that in tf.data |
| 677 | // use case lists and sparse_tensors are not considered sequences. So finished |
| 678 | // checking, structures are the same. |
| 679 | if (!is_seq1) return true; |
| 680 | |
| 681 | if (check_types) { |
| 682 | const PyTypeObject* type1 = o1->ob_type; |
| 683 | const PyTypeObject* type2 = o2->ob_type; |
| 684 | |
| 685 | // We treat two different namedtuples with identical name and fields |
| 686 | // as having the same type. |
| 687 | const PyObject* o1_tuple = IsNamedtuple(o1, true); |
| 688 | if (o1_tuple == nullptr) return false; |
| 689 | const PyObject* o2_tuple = IsNamedtuple(o2, true); |
| 690 | if (o2_tuple == nullptr) { |
| 691 | Py_DECREF(o1_tuple); |
| 692 | return false; |
| 693 | } |
| 694 | bool both_tuples = o1_tuple == Py_True && o2_tuple == Py_True; |
| 695 | Py_DECREF(o1_tuple); |
| 696 | Py_DECREF(o2_tuple); |
| 697 | |
| 698 | if (both_tuples) { |
| 699 | const PyObject* same_tuples = SameNamedtuples(o1, o2); |
| 700 | if (same_tuples == nullptr) return false; |
| 701 | bool not_same_tuples = same_tuples != Py_True; |
| 702 | Py_DECREF(same_tuples); |
| 703 | if (not_same_tuples) { |
| 704 | *is_type_error = true; |
| 705 | *error_msg = tensorflow::strings::StrCat( |
| 706 | "The two namedtuples don't have the same sequence type. " |
| 707 | "First structure ", |
| 708 | PyObjectToString(o1), " has type ", type1->tp_name, |
| 709 | ", while second structure ", PyObjectToString(o2), " has type ", |
| 710 | type2->tp_name); |
| 711 | return true; |
| 712 | } |
no test coverage detected