diff options
author | A. Unique TensorFlower <gardener@tensorflow.org> | 2018-07-27 14:03:25 -0700 |
---|---|---|
committer | TensorFlower Gardener <gardener@tensorflow.org> | 2018-07-27 14:07:05 -0700 |
commit | 81d927bfc7e1e4c7b28aabdb0c64a12cec2833fe (patch) | |
tree | db8ce58fcb792df76624a5e3f94d502a16aba1bf | |
parent | 388d0d860110a19a9d133fe4de85f8f6fa060cde (diff) |
Adding NodeDef names to error messages for better debuggability.
The format used is as follows:
{{node <node_name>}}
PiperOrigin-RevId: 206370355
-rw-r--r-- | tensorflow/compiler/tf2xla/functionalize_control_flow.cc | 37 | ||||
-rw-r--r-- | tensorflow/compiler/tf2xla/functionalize_control_flow_test.cc | 5 | ||||
-rw-r--r-- | tensorflow/compiler/tf2xla/xla_compiler.cc | 13 | ||||
-rw-r--r-- | tensorflow/compiler/tf2xla/xla_compiler_test.cc | 13 | ||||
-rw-r--r-- | tensorflow/contrib/autograph/examples/notebooks/dev_summit_2018_demo.ipynb | 2 | ||||
-rw-r--r-- | tensorflow/core/common_runtime/function_test.cc | 5 | ||||
-rw-r--r-- | tensorflow/core/framework/node_def_util.cc | 13 | ||||
-rw-r--r-- | tensorflow/core/framework/node_def_util.h | 6 | ||||
-rw-r--r-- | tensorflow/core/framework/node_def_util_test.cc | 52 | ||||
-rw-r--r-- | tensorflow/core/framework/op_compatibility_test.cc | 65 | ||||
-rw-r--r-- | tensorflow/core/graph/control_flow.cc | 37 | ||||
-rw-r--r-- | tensorflow/core/graph/control_flow_test.cc | 17 | ||||
-rw-r--r-- | tensorflow/core/lib/core/errors.h | 20 | ||||
-rw-r--r-- | tensorflow/core/util/equal_graph_def_test.cc | 6 | ||||
-rw-r--r-- | tensorflow/docs_src/guide/using_gpu.md | 2 | ||||
-rw-r--r-- | tensorflow/python/client/session.py | 10 | ||||
-rw-r--r-- | tensorflow/python/framework/test_util_test.py | 2 |
17 files changed, 204 insertions, 101 deletions
diff --git a/tensorflow/compiler/tf2xla/functionalize_control_flow.cc b/tensorflow/compiler/tf2xla/functionalize_control_flow.cc index 6cc95149a1..0904778f97 100644 --- a/tensorflow/compiler/tf2xla/functionalize_control_flow.cc +++ b/tensorflow/compiler/tf2xla/functionalize_control_flow.cc @@ -177,8 +177,8 @@ Status CheckNoCycleContains(const Node* node, const int num_nodes) { visited[current_node->id()] = true; for (const Edge* out : current_node->out_edges()) { if (out->dst() == node) { - return errors::Internal("Detect a cycle: Node \"", node->name(), "\"(", - node->def().op(), ") feeds into itself."); + return errors::Internal("Detected a cycle: ", FormatNodeForError(*node), + "(", node->def().op(), ") feeds into itself."); } else if (!visited[out->dst()->id()]) { ready.push_back(out->dst()); } @@ -324,7 +324,7 @@ Status AddMissingFunctionDef(const FunctionDef& fdef, if (library->Find(node.op())) { continue; } - // The function refered by 'SymbolicGradient' node is specified in its + // The function referred by 'SymbolicGradient' node is specified in its // attribute 'f'. if (node.op() == FunctionLibraryDefinition::kGradientOp) { const AttrValue* attr = @@ -437,22 +437,24 @@ Status FunctionalizeLoop(const FunctionLibraryDefinition* lookup_library, continue; } if (enter_merge != nullptr) { - return errors::Internal( - "Enter node for loop-varying argument ", arg.enter->name(), - " has multiple successors: ", enter_merge->dst()->name(), " and ", - e->dst()->name()); + return errors::Internal("Enter node for loop-varying argument ", + FormatNodeForError(*arg.enter), + " has multiple successors: ", + FormatNodeForError(*enter_merge->dst()), + " and ", FormatNodeForError(*e->dst())); } enter_merge = e; } if (enter_merge == nullptr) { return errors::Internal("Enter node for loop-varying argument ", - arg.enter->name(), " has zero successors"); + FormatNodeForError(*arg.enter), + " has zero successors"); } arg.merge = enter_merge->dst(); if (!IsMerge(arg.merge)) { return errors::InvalidArgument( "Successor of Enter node for loop-varying argument ", - arg.merge->name(), + FormatNodeForError(*arg.merge), " is not a Merge node; got: ", arg.merge->type_string()); } @@ -462,7 +464,7 @@ Status FunctionalizeLoop(const FunctionLibraryDefinition* lookup_library, return errors::InvalidArgument( "Unexpected number of inputs to Merge node for loop-varying " "argument ", - arg.merge->name(), "; expected 2, got ", + FormatNodeForError(*arg.merge), "; expected 2, got ", arg.merge->input_types().size()); } TF_RETURN_IF_ERROR(arg.merge->input_node(1 - enter_merge->dst_input(), @@ -470,7 +472,7 @@ Status FunctionalizeLoop(const FunctionLibraryDefinition* lookup_library, if (!IsNextIteration(arg.next_iteration)) { return errors::InvalidArgument( "Expected NextIteration node as input to Merge node; got node ", - arg.next_iteration->name(), " with kind ", + FormatNodeForError(*arg.next_iteration), " with kind ", arg.next_iteration->type_string()); } @@ -481,14 +483,14 @@ Status FunctionalizeLoop(const FunctionLibraryDefinition* lookup_library, switches.find(edge->dst()) != switches.end()) { if (arg.switch_node != nullptr) { return errors::InvalidArgument("Duplicate Switch successors to ", - arg.merge->name()); + FormatNodeForError(*arg.merge)); } arg.switch_node = edge->dst(); } } if (arg.switch_node == nullptr) { return errors::InvalidArgument("Missing Switch successor to ", - arg.merge->name()); + FormatNodeForError(*arg.merge)); } // Update the device on the Identity outputs of the switch to match their @@ -516,14 +518,15 @@ Status FunctionalizeLoop(const FunctionLibraryDefinition* lookup_library, possible_exit.pop_front(); if (IsExit(edge->dst())) { if (arg.exit != nullptr) { - return errors::InvalidArgument("Duplicate Exit successors to ", - arg.switch_node->name()); + return errors::InvalidArgument( + "Duplicate Exit successors to ", + FormatNodeForError(*arg.switch_node)); } arg.exit = edge->dst(); } else { if (!IsIdentity(edge->dst())) { return errors::Unimplemented("General graph between switch (", - arg.switch_node->name(), + FormatNodeForError(*arg.switch_node), ") and exit node of frame ", frame->name, " not supported yet."); } @@ -1470,7 +1473,7 @@ Status FunctionalizeControlFlow(const FunctionLibraryDefinition* lookup_library, if (!unreachable_nodes.empty()) { return errors::InvalidArgument( "The following nodes are unreachable from the source in the graph: ", - tensorflow::str_util::Join(unreachable_nodes, ", ")); + errors::FormatNodeNamesForError(unreachable_nodes)); } // Builds Frames, indexed by name. diff --git a/tensorflow/compiler/tf2xla/functionalize_control_flow_test.cc b/tensorflow/compiler/tf2xla/functionalize_control_flow_test.cc index aae2f8ee5a..ccf249b35d 100644 --- a/tensorflow/compiler/tf2xla/functionalize_control_flow_test.cc +++ b/tensorflow/compiler/tf2xla/functionalize_control_flow_test.cc @@ -1064,7 +1064,10 @@ TEST(FunctionalizeControlFlow, Cycle) { // less -> XlaIf <--> identity. Status status = FunctionalizeControlFlow(graph.get(), &library); EXPECT_FALSE(status.ok()); - EXPECT_TRUE(str_util::StrContains(status.error_message(), "Detect a cycle")) + EXPECT_TRUE(str_util::StrContains(status.error_message(), "Detected a cycle")) + << status.error_message(); + EXPECT_TRUE( + str_util::StrContains(status.error_message(), "{{node cond/Less_5_If}}")) << status.error_message(); } diff --git a/tensorflow/compiler/tf2xla/xla_compiler.cc b/tensorflow/compiler/tf2xla/xla_compiler.cc index 59dcd0870b..226c89bcf1 100644 --- a/tensorflow/compiler/tf2xla/xla_compiler.cc +++ b/tensorflow/compiler/tf2xla/xla_compiler.cc @@ -35,6 +35,7 @@ limitations under the License. #include "tensorflow/core/common_runtime/function.h" #include "tensorflow/core/common_runtime/graph_optimizer.h" #include "tensorflow/core/framework/attr_value_util.h" +#include "tensorflow/core/framework/node_def_util.h" #include "tensorflow/core/graph/algorithm.h" #include "tensorflow/core/graph/graph_constructor.h" #include "tensorflow/core/graph/node_builder.h" @@ -689,12 +690,12 @@ Status ValidateFunctionDef(const FunctionDef* fdef, Status ValidateGraph(const Graph* graph, const FunctionLibraryDefinition& flib_def, const DeviceType& device_type, const string& name) { - auto maybe_error = [&](const string& op, const Status& s) -> Status { + auto maybe_error = [&](const Node* node, const Status& s) -> Status { if (!s.ok()) { return errors::InvalidArgument(strings::StrCat( "Detected unsupported operations when trying to compile graph ", name, - " on ", device_type.type_string(), ": ", op, " (", s.error_message(), - ")")); + " on ", device_type.type_string(), ": ", node->def().op(), " (", + s.error_message(), ")", FormatNodeForError(*node))); } return Status::OK(); }; @@ -707,15 +708,15 @@ Status ValidateGraph(const Graph* graph, Status s; if (fdef) { s = ValidateFunctionDef(fdef, flib_def); - TF_RETURN_IF_ERROR(maybe_error(node->def().op(), s)); + TF_RETURN_IF_ERROR(maybe_error(node, s)); continue; } const OpDef* op_def; s = OpRegistry::Global()->LookUpOpDef(node->def().op(), &op_def); - TF_RETURN_IF_ERROR(maybe_error(node->def().op(), s)); + TF_RETURN_IF_ERROR(maybe_error(node, s)); TF_RETURN_IF_ERROR(ValidateNodeDef(node->def(), *op_def)); s = FindKernelDef(device_type, node->def(), nullptr, nullptr); - TF_RETURN_IF_ERROR(maybe_error(node->def().op(), s)); + TF_RETURN_IF_ERROR(maybe_error(node, s)); } return Status::OK(); } diff --git a/tensorflow/compiler/tf2xla/xla_compiler_test.cc b/tensorflow/compiler/tf2xla/xla_compiler_test.cc index 2fb93be01d..be00ed8813 100644 --- a/tensorflow/compiler/tf2xla/xla_compiler_test.cc +++ b/tensorflow/compiler/tf2xla/xla_compiler_test.cc @@ -312,7 +312,7 @@ TEST_F(XlaCompilerTest, HasSaneErrorOnNonCompileTimeConstantInputToReshape) { str_util::StrContains(status.error_message(), "depends on a parameter")) << status.error_message(); EXPECT_TRUE( - str_util::StrContains(status.error_message(), "[[Node: C = Reshape")) + str_util::StrContains(status.error_message(), "[[{{node C}} = Reshape")) << status.error_message(); } @@ -1077,6 +1077,8 @@ TEST_F(XlaCompilerTest, FunctionWithInvalidOp) { ASSERT_FALSE(status.ok()); EXPECT_TRUE(str_util::StrContains(status.error_message(), "InvalidOp")) << status.error_message(); + EXPECT_TRUE(str_util::StrContains(status.error_message(), "{{node fill_fn}}")) + << status.error_message(); } // Tests a graph which has a node with invalid data type. @@ -1101,6 +1103,8 @@ TEST_F(XlaCompilerTest, NodeWithInvalidDataType) { EXPECT_TRUE(str_util::StrContains(status.error_message(), "is not in the list of allowed values")) << status.error_message(); + EXPECT_TRUE(str_util::StrContains(status.error_message(), "{{node Shape}}")) + << status.error_message(); } TEST_F(XlaCompilerTest, SingleOpWithoutInputs) { @@ -1122,9 +1126,10 @@ TEST_F(XlaCompilerTest, SingleOpWithoutInputs) { status = compiler.CompileGraph(XlaCompiler::CompileOptions(), "NoOp", std::move(graph_copy), args, &result); ASSERT_FALSE(status.ok()); - EXPECT_TRUE(str_util::StrContains(status.error_message(), - "The following nodes are unreachable " - "from the source in the graph: NoOp")) + EXPECT_TRUE( + str_util::StrContains(status.error_message(), + "The following nodes are unreachable " + "from the source in the graph: {{node NoOp}}")) << status.error_message(); } diff --git a/tensorflow/contrib/autograph/examples/notebooks/dev_summit_2018_demo.ipynb b/tensorflow/contrib/autograph/examples/notebooks/dev_summit_2018_demo.ipynb index a3109fa5db..7e9cc54d4c 100644 --- a/tensorflow/contrib/autograph/examples/notebooks/dev_summit_2018_demo.ipynb +++ b/tensorflow/contrib/autograph/examples/notebooks/dev_summit_2018_demo.ipynb @@ -392,7 +392,7 @@ "output_type": "stream", "text": [ "Got error message: assertion failed: [Do not pass zero!]\n", - "\t [[Node: f/Assert/Assert = Assert[T=[DT_STRING], summarize=3, _device=\"/job:localhost/replica:0/task:0/device:CPU:0\"](f/NotEqual, f/Assert/Assert/data_0)]]\n" + "\t [[{{node f/Assert/Assert}} = Assert[T=[DT_STRING], summarize=3, _device=\"/job:localhost/replica:0/task:0/device:CPU:0\"](f/NotEqual, f/Assert/Assert/data_0)]]\n" ] } ], diff --git a/tensorflow/core/common_runtime/function_test.cc b/tensorflow/core/common_runtime/function_test.cc index 1e837e9a7e..120f480198 100644 --- a/tensorflow/core/common_runtime/function_test.cc +++ b/tensorflow/core/common_runtime/function_test.cc @@ -1019,8 +1019,9 @@ TEST_F(FunctionLibraryRuntimeTest, Error_BadControlFlow) { DCHECK_EQ(x.dtype(), DT_INT32); Tensor y; HasError(InstantiateAndRun(flr0_, "InvalidControlFlow", {}, {x}, {&y}), - "The node 'add' has inputs from different frames. The input 'enter' " - "is in frame 'while'. The input 'i' is in frame ''."); + "{{node add}} has inputs from different frames. The input" + " {{node enter}} is in frame 'while'. The input {{node i}} is in" + " frame ''."); } TEST_F(FunctionLibraryRuntimeTest, Gradient_XTimesTwo) { diff --git a/tensorflow/core/framework/node_def_util.cc b/tensorflow/core/framework/node_def_util.cc index e8ea904ebd..0bd79366eb 100644 --- a/tensorflow/core/framework/node_def_util.cc +++ b/tensorflow/core/framework/node_def_util.cc @@ -86,7 +86,8 @@ string AttrSlice::SummarizeNode() const { string SummarizeNode(const Node& node) { return SummarizeNodeDef(node.def()); } string SummarizeNodeDef(const NodeDef& node_def) { - string ret = strings::StrCat(node_def.name(), " = ", node_def.op(), "["); + string ret = strings::StrCat(FormatNodeDefForError(node_def), " = ", + node_def.op(), "["); strings::StrAppend(&ret, SummarizeAttrsHelper(node_def, node_def.device())); strings::StrAppend(&ret, "]("); @@ -101,6 +102,14 @@ string SummarizeNodeDef(const NodeDef& node_def) { return ret; } +string FormatNodeForError(const Node& node) { + return FormatNodeDefForError(node.def()); +} + +string FormatNodeDefForError(const NodeDef& node_def) { + return errors::FormatNodeNameForError(node_def.name()); +} + const AttrValue* AttrSlice::Find(StringPiece attr_name) const { // Currently, the collection used for NodeDef::attr() (google::protobuf::Map) // requires that the keys used for lookups have type 'const string&'. Because @@ -634,7 +643,7 @@ Status ValidateExternalNodeDefSyntax(const NodeDef& node_def) { Status AttachDef(const Status& status, const NodeDef& node_def) { Status ret = status; errors::AppendToMessage( - &ret, strings::StrCat(" [[Node: ", SummarizeNodeDef(node_def), "]]")); + &ret, strings::StrCat(" [[", SummarizeNodeDef(node_def), "]]")); return ret; } diff --git a/tensorflow/core/framework/node_def_util.h b/tensorflow/core/framework/node_def_util.h index 64c8b386e8..c012b7c3d3 100644 --- a/tensorflow/core/framework/node_def_util.h +++ b/tensorflow/core/framework/node_def_util.h @@ -50,6 +50,12 @@ extern const char* const kColocationGroupPrefix; string SummarizeNode(const Node& node); string SummarizeNodeDef(const NodeDef& node_def); +// Produces a formatted string pattern from the node which can uniquely identify +// this node upstream to produce an informative error message. The pattern +// followed is: {{node <node_name>}} +string FormatNodeForError(const Node& node); +string FormatNodeDefForError(const NodeDef& node_def); + typedef protobuf::Map<string, AttrValue> AttrValueMap; // Adds an attr with name <name> and value <value> to *node_def. diff --git a/tensorflow/core/framework/node_def_util_test.cc b/tensorflow/core/framework/node_def_util_test.cc index 35b7b2272b..74cc594863 100644 --- a/tensorflow/core/framework/node_def_util_test.cc +++ b/tensorflow/core/framework/node_def_util_test.cc @@ -20,6 +20,8 @@ limitations under the License. #include "tensorflow/core/framework/node_def_builder.h" #include "tensorflow/core/framework/op_def_builder.h" #include "tensorflow/core/framework/op_def_util.h" +#include "tensorflow/core/graph/graph.h" +#include "tensorflow/core/graph/node_builder.h" #include "tensorflow/core/lib/core/errors.h" #include "tensorflow/core/lib/core/status_test_util.h" #include "tensorflow/core/lib/strings/str_util.h" @@ -79,7 +81,7 @@ TEST(NodeDefUtilTest, In) { )proto"); ExpectSuccess(node_def, op); - EXPECT_EQ("n = In[T=DT_FLOAT](a)", SummarizeNodeDef(node_def)); + EXPECT_EQ("{{node n}} = In[T=DT_FLOAT](a)", SummarizeNodeDef(node_def)); // Mismatching Op names. NodeDef bad = node_def; @@ -144,7 +146,7 @@ TEST(NodeDefUtilTest, Out) { )proto"); ExpectSuccess(node_def, op); - EXPECT_EQ("n = Out[T=DT_INT32]()", SummarizeNodeDef(node_def)); + EXPECT_EQ("{{node n}} = Out[T=DT_INT32]()", SummarizeNodeDef(node_def)); // Non-number type. NodeDef bad = node_def; @@ -164,7 +166,7 @@ TEST(NodeDefUtilTest, Enum) { )proto"); ExpectSuccess(node_def, op); - EXPECT_EQ("n = Enum[e=\"apple\"]()", SummarizeNodeDef(node_def)); + EXPECT_EQ("{{node n}} = Enum[e=\"apple\"]()", SummarizeNodeDef(node_def)); NodeDef good = node_def; good.clear_attr(); @@ -191,7 +193,8 @@ TEST(NodeDefUtilTest, SameIn) { )proto"); ExpectSuccess(node_def, op); - EXPECT_EQ("n = SameIn[N=2, T=DT_DOUBLE](a, b)", SummarizeNodeDef(node_def)); + EXPECT_EQ("{{node n}} = SameIn[N=2, T=DT_DOUBLE](a, b)", + SummarizeNodeDef(node_def)); // Illegal type NodeDef bad = ToNodeDef(R"proto( @@ -220,7 +223,7 @@ TEST(NodeDefUtilTest, AnyIn) { )proto"); ExpectSuccess(node_def, op); - EXPECT_EQ("n = AnyIn[T=[DT_INT32, DT_STRING]](a, b)", + EXPECT_EQ("{{node n}} = AnyIn[T=[DT_INT32, DT_STRING]](a, b)", SummarizeNodeDef(node_def)); const NodeDef bad = ToNodeDef(R"proto( @@ -243,13 +246,14 @@ TEST(NodeDefUtilTest, Device) { const NodeDef node_def1 = ToNodeDef(NodeDefBuilder("d", &op_def1).Device("/cpu:17")); ExpectSuccess(node_def1, op_def1); - EXPECT_EQ("d = None[_device=\"/cpu:17\"]()", SummarizeNodeDef(node_def1)); + EXPECT_EQ("{{node d}} = None[_device=\"/cpu:17\"]()", + SummarizeNodeDef(node_def1)); const OpDef op_def2 = ToOpDef(OpDefBuilder("WithAttr").Attr("v: int")); const NodeDef node_def2 = ToNodeDef(NodeDefBuilder("d", &op_def2).Attr("v", 7).Device("/cpu:5")); ExpectSuccess(node_def2, op_def2); - EXPECT_EQ("d = WithAttr[v=7, _device=\"/cpu:5\"]()", + EXPECT_EQ("{{node d}} = WithAttr[v=7, _device=\"/cpu:5\"]()", SummarizeNodeDef(node_def2)); } @@ -284,7 +288,7 @@ TEST(NodeDefUtilTest, ValidSyntax) { )proto"); ExpectValidSyntax(node_def_explicit_inputs); - EXPECT_EQ("n = AnyIn[T=[DT_INT32, DT_STRING]](a:0, b:123)", + EXPECT_EQ("{{node n}} = AnyIn[T=[DT_INT32, DT_STRING]](a:0, b:123)", SummarizeNodeDef(node_def_explicit_inputs)); const NodeDef node_def_partial_shape = ToNodeDef(R"proto( @@ -379,7 +383,7 @@ TEST(NameRangesForNodeTest, Simple) { EXPECT_EQ(NameRangeMap({{"a", {0, 1}}, {"b", {1, 2}}}), inputs); EXPECT_EQ(NameRangeMap({{"c", {0, 1}}, {"d", {1, 2}}}), outputs); - EXPECT_EQ("simple = Simple[](a, b)", SummarizeNodeDef(node_def)); + EXPECT_EQ("{{node simple}} = Simple[](a, b)", SummarizeNodeDef(node_def)); OpDef bad_op_def = op_def; bad_op_def.mutable_input_arg(0)->clear_type(); @@ -399,7 +403,7 @@ TEST(NameRangesForNodeTest, Polymorphic) { TF_EXPECT_OK(NameRangesForNode(node_def1, op_def, &inputs, &outputs)); EXPECT_EQ(NameRangeMap({{"a", {0, 1}}, {"b", {1, 2}}}), inputs); EXPECT_EQ(NameRangeMap({{"c", {0, 1}}}), outputs); - EXPECT_EQ("poly = Polymorphic[T=DT_INT32](a, b)", + EXPECT_EQ("{{node poly}} = Polymorphic[T=DT_INT32](a, b)", SummarizeNodeDef(node_def1)); const NodeDef node_def2 = ToNodeDef(NodeDefBuilder("poly", &op_def) @@ -408,7 +412,8 @@ TEST(NameRangesForNodeTest, Polymorphic) { TF_EXPECT_OK(NameRangesForNode(node_def2, op_def, &inputs, &outputs)); EXPECT_EQ(NameRangeMap({{"a", {0, 1}}, {"b", {1, 2}}}), inputs); EXPECT_EQ(NameRangeMap({{"c", {0, 1}}}), outputs); - EXPECT_EQ("poly = Polymorphic[T=DT_BOOL](a, b)", SummarizeNodeDef(node_def2)); + EXPECT_EQ("{{node poly}} = Polymorphic[T=DT_BOOL](a, b)", + SummarizeNodeDef(node_def2)); } TEST(NameRangesForNodeTest, NRepeats) { @@ -431,7 +436,8 @@ TEST(NameRangesForNodeTest, NRepeats) { EXPECT_EQ(NameRangeMap({{"c", {0, 1}}, {"d", {1, 5}}, {"e", {5, 8}}}), outputs); EXPECT_EQ( - "nr = NRepeats[M=3, N=4, T=DT_FLOAT](a, a:1, a:2, a:3, b, b:1, b:2, b:3)", + "{{node nr}} = NRepeats[M=3, N=4, T=DT_FLOAT](a, a:1, a:2, a:3, b, b:1, " + "b:2, b:3)", SummarizeNodeDef(node_def1)); const NodeDef node_def2 = ToNodeDef(NodeDefBuilder("nr", &op_def) @@ -442,7 +448,7 @@ TEST(NameRangesForNodeTest, NRepeats) { EXPECT_EQ(NameRangeMap({{"a", {0, 2}}, {"b", {2, 4}}}), inputs); EXPECT_EQ(NameRangeMap({{"c", {0, 1}}, {"d", {1, 3}}, {"e", {3, 10}}}), outputs); - EXPECT_EQ("nr = NRepeats[M=7, N=2, T=DT_DOUBLE](a, a:1, b, b:1)", + EXPECT_EQ("{{node nr}} = NRepeats[M=7, N=2, T=DT_DOUBLE](a, a:1, b, b:1)", SummarizeNodeDef(node_def2)); NodeDef bad_node_def = node_def2; @@ -471,7 +477,7 @@ TEST(NameRangesForNodeTest, TypeList) { EXPECT_EQ(NameRangeMap({{"c", {0, 4}}, {"d", {4, 7}}, {"e", {7, 9}}}), outputs); EXPECT_EQ( - "tl = TypeList[T1=[DT_BOOL, DT_FLOAT]," + "{{node tl}} = TypeList[T1=[DT_BOOL, DT_FLOAT]," " T2=[DT_FLOAT, DT_FLOAT, DT_FLOAT, DT_FLOAT]," " T3=[DT_INT32, DT_DOUBLE, DT_STRING]](a, a:1, b, b:1, b:2, b:3)", SummarizeNodeDef(node_def1)); @@ -485,7 +491,8 @@ TEST(NameRangesForNodeTest, TypeList) { EXPECT_EQ(NameRangeMap({{"c", {0, 1}}, {"d", {1, 3}}, {"e", {3, 10}}}), outputs); EXPECT_EQ( - "tl = TypeList[T1=[DT_INT32, DT_INT32, DT_INT32, DT_INT32, DT_INT32," + "{{node tl}} = TypeList[T1=[DT_INT32, DT_INT32, DT_INT32, DT_INT32, " + "DT_INT32," " DT_INT32, DT_INT32], T2=[DT_DOUBLE], T3=[DT_DOUBLE, DT_STRING]]" "(a, a:1, a:2, a:3, a:4, a:5, a:6, b)", SummarizeNodeDef(node_def2)); @@ -509,5 +516,20 @@ TEST(AddPrefixAndSuffixToNode, Enter) { EXPECT_EQ("prefix/test_frame/suffix", frame_name); } +TEST(FormatNodeForErrorTest, Node) { + Graph g(OpRegistry::Global()); + Node* node; + TF_CHECK_OK(NodeBuilder("enter", "NoOp").Finalize(&g, &node)); + EXPECT_EQ("{{node enter}}", FormatNodeForError(*node)); +} + +TEST(FormatNodeForErrorTest, NodeDef) { + NodeDef node_def; + node_def.set_name("enter"); + node_def.set_op("Enter"); + AddNodeAttr("frame_name", "test_frame", &node_def); + EXPECT_EQ("{{node enter}}", FormatNodeDefForError(node_def)); +} + } // namespace } // namespace tensorflow diff --git a/tensorflow/core/framework/op_compatibility_test.cc b/tensorflow/core/framework/op_compatibility_test.cc index c782480f1f..140f201085 100644 --- a/tensorflow/core/framework/op_compatibility_test.cc +++ b/tensorflow/core/framework/op_compatibility_test.cc @@ -209,8 +209,8 @@ TEST_F(OpCompatibilityTest, Same) { .Finalize(node_def())); ExpectSuccess(*RegisteredOpDef()); EXPECT_EQ( - "same = Same[N=3, T=DT_FLOAT, TList=[DT_BOOL, DT_BOOL]](a, b, c, c:1, " - "c:2, d, d:1, d:2, e, e:1)", + "{{node same}} = Same[N=3, T=DT_FLOAT, TList=[DT_BOOL, DT_BOOL]](a, b, " + "c, c:1, c:2, d, d:1, d:2, e, e:1)", Result()); } @@ -224,7 +224,7 @@ TEST_F(OpCompatibilityTest, AddAttr) { OpDefBuilder("AddAttr").Output("ndef: string").Finalize(&old_op)); TF_ASSERT_OK(NodeDefBuilder("add_attr", &old_op.op_def).Finalize(node_def())); ExpectSuccess(old_op.op_def); - EXPECT_EQ("add_attr = AddAttr[a=42]()", Result()); + EXPECT_EQ("{{node add_attr}} = AddAttr[a=42]()", Result()); } // Should be able to make an attr restriction less strict. @@ -241,7 +241,7 @@ TEST_F(OpCompatibilityTest, LessStrict) { .Attr("a", "B") .Finalize(node_def())); ExpectSuccess(old_op.op_def); - EXPECT_EQ("less_strict = LessStrict[a=\"B\"]()", Result()); + EXPECT_EQ("{{node less_strict}} = LessStrict[a=\"B\"]()", Result()); } // Should be able to remove an attr restriction. @@ -259,7 +259,8 @@ TEST_F(OpCompatibilityTest, RemoveRestriction) { .Attr("a", DT_INT32) .Finalize(node_def())); ExpectSuccess(old_op.op_def); - EXPECT_EQ("remove_restriction = RemoveRestriction[a=DT_INT32]()", Result()); + EXPECT_EQ("{{node remove_restriction}} = RemoveRestriction[a=DT_INT32]()", + Result()); } // Should be able to change the order of attrs. @@ -278,7 +279,7 @@ TEST_F(OpCompatibilityTest, AttrOrder) { .Attr("a", 7) .Finalize(node_def())); ExpectSuccess(old_op.op_def); - EXPECT_EQ("attr_order = AttrOrder[a=7, b=true]()", Result()); + EXPECT_EQ("{{node attr_order}} = AttrOrder[a=7, b=true]()", Result()); } // Should be able to make an input/output polymorphic. @@ -299,7 +300,8 @@ TEST_F(OpCompatibilityTest, TypePolymorphic) { .Input(FakeInput()) .Finalize(node_def())); ExpectSuccess(old_op.op_def); - EXPECT_EQ("type_polymorphic = TypePolymorphic[T=DT_INT32](a)", Result()); + EXPECT_EQ("{{node type_polymorphic}} = TypePolymorphic[T=DT_INT32](a)", + Result()); } // Should be able to make a single input/output into a list. @@ -320,7 +322,7 @@ TEST_F(OpCompatibilityTest, MakeList) { .Input(FakeInput()) .Finalize(node_def())); ExpectSuccess(old_op.op_def); - EXPECT_EQ("make_list = MakeList[N=1](a)", Result()); + EXPECT_EQ("{{node make_list}} = MakeList[N=1](a)", Result()); } // Should be able to make a single input/output into a polymorphic list. @@ -343,7 +345,8 @@ TEST_F(OpCompatibilityTest, MakePolyList) { .Input(FakeInput()) .Finalize(node_def())); ExpectSuccess(old_op.op_def); - EXPECT_EQ("make_poly_list = MakePolyList[N=1, T=DT_INT32](a)", Result()); + EXPECT_EQ("{{node make_poly_list}} = MakePolyList[N=1, T=DT_INT32](a)", + Result()); } // Should be able to make a single input/output into an arbitrary list. @@ -364,7 +367,7 @@ TEST_F(OpCompatibilityTest, MakeAnyList) { .Input(FakeInput()) .Finalize(node_def())); ExpectSuccess(old_op.op_def); - EXPECT_EQ("make_any_list = MakeAnyList[T=[DT_INT32]](a)", Result()); + EXPECT_EQ("{{node make_any_list}} = MakeAnyList[T=[DT_INT32]](a)", Result()); } // Should be able to make a single polymorphic input/output into a list of @@ -387,7 +390,8 @@ TEST_F(OpCompatibilityTest, PolyIntoList) { .Input(FakeInput(DT_INT32)) .Finalize(node_def())); ExpectSuccess(old_op.op_def); - EXPECT_EQ("poly_into_list = PolyIntoList[N=1, T=DT_INT32](a)", Result()); + EXPECT_EQ("{{node poly_into_list}} = PolyIntoList[N=1, T=DT_INT32](a)", + Result()); } // Should be able to make a multiple inputs/outputs into a list with @@ -413,7 +417,7 @@ TEST_F(OpCompatibilityTest, MakeMultipleSameList) { .Input(FakeInput()) .Finalize(node_def())); ExpectSuccess(old_op.op_def); - EXPECT_EQ("make_list = MakeMultipleSameList[N=2](a, b)", Result()); + EXPECT_EQ("{{node make_list}} = MakeMultipleSameList[N=2](a, b)", Result()); } // Changing from int32, float -> T @@ -437,8 +441,9 @@ TEST_F(OpCompatibilityTest, MakeMultipleAnyList) { .Input(FakeInput()) .Finalize(node_def())); ExpectSuccess(old_op.op_def); - EXPECT_EQ("make_list = MakeMultipleAnyList[T=[DT_INT32, DT_FLOAT]](a, b)", - Result()); + EXPECT_EQ( + "{{node make_list}} = MakeMultipleAnyList[T=[DT_INT32, DT_FLOAT]](a, b)", + Result()); } // Should be able to change the name of an input/output. @@ -455,7 +460,7 @@ TEST_F(OpCompatibilityTest, ChangeName) { .Input(FakeInput()) .Finalize(node_def())); ExpectSuccess(old_op.op_def); - EXPECT_EQ("change_name = ChangeName[](a)", Result()); + EXPECT_EQ("{{node change_name}} = ChangeName[](a)", Result()); } // Should be able to add an input/output of type @@ -473,7 +478,7 @@ TEST_F(OpCompatibilityTest, AddNInts) { TF_ASSERT_OK( NodeDefBuilder("add_n_ints", &old_op.op_def).Finalize(node_def())); ExpectSuccess(old_op.op_def); - EXPECT_EQ("add_n_ints = AddNInts[N=0]()", Result()); + EXPECT_EQ("{{node add_n_ints}} = AddNInts[N=0]()", Result()); } // Should be able to add an input/output of type N * T @@ -492,7 +497,7 @@ TEST_F(OpCompatibilityTest, AddNSame) { TF_ASSERT_OK( NodeDefBuilder("add_n_same", &old_op.op_def).Finalize(node_def())); ExpectSuccess(old_op.op_def); - EXPECT_EQ("add_n_same = AddNSame[N=0, T=DT_BOOL]()", Result()); + EXPECT_EQ("{{node add_n_same}} = AddNSame[N=0, T=DT_BOOL]()", Result()); } // Should be able to add an input/output of type N * T @@ -517,8 +522,10 @@ TEST_F(OpCompatibilityTest, AddNSameAsExisting) { .Input(FakeInput(DT_STRING)) .Finalize(node_def())); ExpectSuccess(old_op.op_def); - EXPECT_EQ("add_n_same_as_existing = AddNSameAsExisting[N=0, T=DT_STRING](a)", - Result()); + EXPECT_EQ( + "{{node add_n_same_as_existing}} = AddNSameAsExisting[N=0, " + "T=DT_STRING](a)", + Result()); } // Should be able to add an input/output of type T @@ -536,7 +543,7 @@ TEST_F(OpCompatibilityTest, AddAnyList) { TF_ASSERT_OK( NodeDefBuilder("add_any_list", &old_op.op_def).Finalize(node_def())); ExpectSuccess(old_op.op_def); - EXPECT_EQ("add_any_list = AddAnyList[T=[]]()", Result()); + EXPECT_EQ("{{node add_any_list}} = AddAnyList[T=[]]()", Result()); } // Should be able to allow shorter lists. @@ -557,8 +564,10 @@ TEST_F(OpCompatibilityTest, ShorterAnyList) { .Input(FakeInput(2, DT_BOOL)) .Finalize(node_def())); ExpectSuccess(old_op.op_def); - EXPECT_EQ("shorter_any_list = ShorterAnyList[T=[DT_BOOL, DT_BOOL]](a, a:1)", - Result()); + EXPECT_EQ( + "{{node shorter_any_list}} = ShorterAnyList[T=[DT_BOOL, DT_BOOL]](a, " + "a:1)", + Result()); } REGISTER_OP("ShorterSameList") @@ -578,7 +587,8 @@ TEST_F(OpCompatibilityTest, ShorterSameList) { .Input(FakeInput(2)) .Finalize(node_def())); ExpectSuccess(old_op.op_def); - EXPECT_EQ("shorter_same_list = ShorterSameList[N=2](a, a:1)", Result()); + EXPECT_EQ("{{node shorter_same_list}} = ShorterSameList[N=2](a, a:1)", + Result()); } // Can remove a restriction to an attr @@ -597,7 +607,7 @@ TEST_F(OpCompatibilityTest, AttrRemoveRestriction) { .Attr("t", DT_INT32) .Finalize(node_def())); ExpectSuccess(old_op.op_def); - EXPECT_EQ("remove_restriction = AttrRemoveRestriction[t=DT_INT32]()", + EXPECT_EQ("{{node remove_restriction}} = AttrRemoveRestriction[t=DT_INT32]()", Result()); } @@ -619,7 +629,8 @@ TEST_F(OpCompatibilityTest, AttrLessRestrictive) { .Attr("t", DT_INT32) .Finalize(node_def())); ExpectSuccess(old_op.op_def); - EXPECT_EQ("less_restrictive = AttrLessRestrictive[t=DT_INT32]()", Result()); + EXPECT_EQ("{{node less_restrictive}} = AttrLessRestrictive[t=DT_INT32]()", + Result()); } // Can remove a minimum from an attr. @@ -637,7 +648,7 @@ TEST_F(OpCompatibilityTest, AttrRemoveMin) { .Attr("n", 4) .Finalize(node_def())); ExpectSuccess(old_op.op_def); - EXPECT_EQ("remove_min = AttrRemoveMin[n=4]()", Result()); + EXPECT_EQ("{{node remove_min}} = AttrRemoveMin[n=4]()", Result()); } // Can lower the minimum on an attr. @@ -655,7 +666,7 @@ TEST_F(OpCompatibilityTest, AttrLowerMin) { .Attr("n", 4) .Finalize(node_def())); ExpectSuccess(old_op.op_def); - EXPECT_EQ("lower_min = AttrLowerMin[n=4]()", Result()); + EXPECT_EQ("{{node lower_min}} = AttrLowerMin[n=4]()", Result()); } // Can make a ref input into a non-ref input. diff --git a/tensorflow/core/graph/control_flow.cc b/tensorflow/core/graph/control_flow.cc index 1778e48ef6..8e1e56d29b 100644 --- a/tensorflow/core/graph/control_flow.cc +++ b/tensorflow/core/graph/control_flow.cc @@ -18,6 +18,7 @@ limitations under the License. #include <deque> #include <vector> +#include "tensorflow/core/framework/node_def_util.h" #include "tensorflow/core/framework/types.h" #include "tensorflow/core/graph/node_builder.h" #include "tensorflow/core/lib/core/errors.h" @@ -54,10 +55,11 @@ Status ValidateControlFlowInfo(const Graph* graph, frame.parent = parent; frame.name = cf.frame_name; } else if (frame.parent != parent) { - return errors::InvalidArgument( + return errors::Internal( "Invalid loop structure: Mismatched parent frames for \"", cf.frame_name, "\": \"", parent->name, "\" vs \"", frame.parent->name, - "\". This is an internal bug, please file a bug report with " + "\". The node giving this error: ", FormatNodeForError(*node), + "This is an internal bug, please file a bug report with " "instructions on how to reproduce the error."); } if (IsLoopCond(node)) { @@ -69,9 +71,9 @@ Status ValidateControlFlowInfo(const Graph* graph, !str_util::StrContains(node->name(), "LoopCounter")) { return errors::InvalidArgument( "Invalid loop structure: Loop \"", cf.frame_name, - "\" has more than one LoopCond node: \"", node->name(), "\" and \"", - frame.loop_cond->name(), - "\". This is an internal bug, please file a bug report with " + "\" has more than one LoopCond node: ", FormatNodeForError(*node), + " and ", FormatNodeForError(*frame.loop_cond), + ". This is an internal bug, please file a bug report with " "instructions on how to reproduce the error."); } frame.loop_cond = node; @@ -135,12 +137,11 @@ Status BuildControlFlowInfo(const Graph* g, std::vector<ControlFlowInfo>* info, const string& parent_frame = (*info)[out_parent->id()].frame_name; if (parent_frame != frame_name) { return errors::InvalidArgument( - "The node '", out->name(), - "' has inputs from different " - "frames. The input '", - curr_node->name(), "' is in frame '", frame_name, - "'. The input '", parent_nodes[out->id()]->name(), - "' is in frame '", parent_frame, "'."); + FormatNodeForError(*out), + " has inputs from different frames. The input ", + FormatNodeForError(*curr_node), " is in frame '", frame_name, + "'. The input ", FormatNodeForError(*parent_nodes[out->id()]), + " is in frame '", parent_frame, "'."); } } else { out_info->frame = out; @@ -148,7 +149,8 @@ Status BuildControlFlowInfo(const Graph* g, std::vector<ControlFlowInfo>* info, TF_RETURN_IF_ERROR( GetNodeAttr(out->attrs(), "frame_name", &out_info->frame_name)); if (out_info->frame_name.empty()) { - return errors::InvalidArgument("The Enter node ", out->name(), + return errors::InvalidArgument("The Enter ", + FormatNodeForError(*out), " must have a frame name."); } } @@ -156,12 +158,11 @@ Status BuildControlFlowInfo(const Graph* g, std::vector<ControlFlowInfo>* info, if (is_visited) { if (out_info->frame_name != frame_name) { return errors::InvalidArgument( - "The node '", out->name(), - "' has inputs from different " - "frames. The input '", - curr_node->name(), "' is in frame '", frame_name, - "'. The input '", parent_nodes[out->id()]->name(), - "' is in frame '", out_info->frame_name, "'."); + FormatNodeForError(*out), + " has inputs from different frames. The input ", + FormatNodeForError(*curr_node), " is in frame '", frame_name, + "'. The input ", FormatNodeForError(*parent_nodes[out->id()]), + " is in frame '", out_info->frame_name, "'."); } } else { out_info->frame = frame; diff --git a/tensorflow/core/graph/control_flow_test.cc b/tensorflow/core/graph/control_flow_test.cc index eb7937400f..803c757c3f 100644 --- a/tensorflow/core/graph/control_flow_test.cc +++ b/tensorflow/core/graph/control_flow_test.cc @@ -63,6 +63,15 @@ TEST(ValidateControlFlowTest, InputsFromDifferentFrames) { EXPECT_TRUE(str_util::StrContains(status.error_message(), "has inputs from different frames")) << status.error_message(); + EXPECT_TRUE(str_util::StrContains(status.error_message(), + "{{node outer/body/inner/Merge}}")) + << status.error_message(); + EXPECT_TRUE(str_util::StrContains(status.error_message(), + "{{node outer/body/inner/Enter}}")) + << status.error_message(); + EXPECT_TRUE( + str_util::StrContains(status.error_message(), "{{node outer/Switch}}")) + << status.error_message(); } TEST(ValidateControlFlowTest, MismatchedParentFrames) { @@ -102,6 +111,8 @@ TEST(ValidateControlFlowTest, MismatchedParentFrames) { EXPECT_TRUE( str_util::StrContains(status.error_message(), "Mismatched parent frames")) << status.error_message(); + EXPECT_TRUE(str_util::StrContains(status.error_message(), "{{node Enter2}}")) + << status.error_message(); } TEST(ValidateControlFlowTest, TwoLoopCond) { @@ -125,6 +136,12 @@ TEST(ValidateControlFlowTest, TwoLoopCond) { EXPECT_TRUE(str_util::StrContains(status.error_message(), "more than one LoopCond node")) << status.error_message(); + EXPECT_TRUE( + str_util::StrContains(status.error_message(), "{{node sub/LoopCond}}")) + << status.error_message(); + EXPECT_TRUE( + str_util::StrContains(status.error_message(), "{{node LoopCond}}")) + << status.error_message(); } } // namespace diff --git a/tensorflow/core/lib/core/errors.h b/tensorflow/core/lib/core/errors.h index 51c09032df..a631d9815a 100644 --- a/tensorflow/core/lib/core/errors.h +++ b/tensorflow/core/lib/core/errors.h @@ -19,6 +19,7 @@ limitations under the License. #include <sstream> #include "tensorflow/core/lib/core/status.h" +#include "tensorflow/core/lib/strings/str_util.h" #include "tensorflow/core/lib/strings/strcat.h" #include "tensorflow/core/platform/logging.h" #include "tensorflow/core/platform/macros.h" @@ -118,6 +119,25 @@ DECLARE_ERROR(Unauthenticated, UNAUTHENTICATED) #undef DECLARE_ERROR +// Produces a formatted string pattern from the name which can uniquely identify +// this node upstream to produce an informative error message. The pattern +// followed is: {{node <name>}} +// Note: The pattern below determines the regex _NODEDEF_NAME_RE in the file +// tensorflow/python/client/session.py +// LINT.IfChange +inline string FormatNodeNameForError(const string& name) { + return strings::StrCat("{{node ", name, "}}"); +} +// LINT.ThenChange(//tensorflow/python/client/session.py) +template <typename T> +string FormatNodeNamesForError(const T& names) { + ::tensorflow::str_util::Formatter<string> f( + [](string* output, const string& s) { + ::tensorflow::strings::StrAppend(output, FormatNodeNameForError(s)); + }); + return ::tensorflow::str_util::Join(names, ", ", f); +} + // The CanonicalCode() for non-errors. using ::tensorflow::error::OK; diff --git a/tensorflow/core/util/equal_graph_def_test.cc b/tensorflow/core/util/equal_graph_def_test.cc index c54540332e..77ca8eaec3 100644 --- a/tensorflow/core/util/equal_graph_def_test.cc +++ b/tensorflow/core/util/equal_graph_def_test.cc @@ -85,7 +85,7 @@ TEST_F(EqualGraphDefTest, NoMatch) { Input(e_.opts().WithName("A")); Input(a_.opts().WithName("B")); EXPECT_FALSE(Match()); - EXPECT_EQ("Did not find expected node 'A = Input[]()'", diff_); + EXPECT_EQ("Did not find expected node '{{node A}} = Input[]()'", diff_); } TEST_F(EqualGraphDefTest, MissingNode) { @@ -93,7 +93,7 @@ TEST_F(EqualGraphDefTest, MissingNode) { Input(e_.opts().WithName("B")); Input(a_.opts().WithName("A")); EXPECT_FALSE(Match()); - EXPECT_EQ("Did not find expected node 'B = Input[]()'", diff_); + EXPECT_EQ("Did not find expected node '{{node B}} = Input[]()'", diff_); } TEST_F(EqualGraphDefTest, ExtraNode) { @@ -101,7 +101,7 @@ TEST_F(EqualGraphDefTest, ExtraNode) { Input(a_.opts().WithName("A")); Input(a_.opts().WithName("B")); EXPECT_FALSE(Match()); - EXPECT_EQ("Found unexpected node 'B = Input[]()'", diff_); + EXPECT_EQ("Found unexpected node '{{node B}} = Input[]()'", diff_); } TEST_F(EqualGraphDefTest, NodeOrder) { diff --git a/tensorflow/docs_src/guide/using_gpu.md b/tensorflow/docs_src/guide/using_gpu.md index c429ca4750..c0218fd12e 100644 --- a/tensorflow/docs_src/guide/using_gpu.md +++ b/tensorflow/docs_src/guide/using_gpu.md @@ -143,7 +143,7 @@ If the device you have specified does not exist, you will get ``` InvalidArgumentError: Invalid argument: Cannot assign a device to node 'b': Could not satisfy explicit device specification '/device:GPU:2' - [[Node: b = Const[dtype=DT_FLOAT, value=Tensor<type: float shape: [3,2] + [[{{node b}} = Const[dtype=DT_FLOAT, value=Tensor<type: float shape: [3,2] values: 1 2 3...>, _device="/device:GPU:2"]()]] ``` diff --git a/tensorflow/python/client/session.py b/tensorflow/python/client/session.py index 861230e5a0..f8e20e1b89 100644 --- a/tensorflow/python/client/session.py +++ b/tensorflow/python/client/session.py @@ -1235,8 +1235,12 @@ class BaseSession(SessionInterface): return _fetch_handler_run - # Captures the name of a node in an error status. - _NODEDEF_NAME_RE = re.compile(r'\[\[Node: ([^ ]*?) =') + # Captures the name of a node in an error status. The regex below matches + # both the old and the new formats: + # Old format: [[Node: <node_name> = ...]] + # New format: [[{{node <node_name>}} = ...]] + _NODEDEF_NAME_RE = re.compile( + r'\[\[(Node: )?(\{\{node )?([^\} ]*)(\}\})?\s*=') def _do_run(self, handle, target_list, fetch_list, feed_dict, options, run_metadata): @@ -1291,7 +1295,7 @@ class BaseSession(SessionInterface): node_def = None op = None if m is not None: - node_name = m.group(1) + node_name = m.group(3) try: op = self._graph.get_operation_by_name(node_name) node_def = op.node_def diff --git a/tensorflow/python/framework/test_util_test.py b/tensorflow/python/framework/test_util_test.py index 122c14c847..f983cbef04 100644 --- a/tensorflow/python/framework/test_util_test.py +++ b/tensorflow/python/framework/test_util_test.py @@ -73,7 +73,7 @@ class TestUtilTest(test_util.TensorFlowTestCase): test_util.assert_equal_graph_def(def_57, def_75) # Compare two unequal graphs with self.assertRaisesRegexp(AssertionError, - r"^Found unexpected node 'seven"): + r"^Found unexpected node '{{node seven}}"): test_util.assert_equal_graph_def(def_57, def_empty) def testIsGoogleCudaEnabled(self): |