From 37676614285265bbc9455fde36ce5408f2651f34 Mon Sep 17 00:00:00 2001 From: Cyrus Harrison Date: Fri, 5 Feb 2021 12:25:33 -0800 Subject: [PATCH 1/4] add Node::to_summary_string --- src/libs/conduit/conduit_node.cpp | 335 ++++++++++++++++++++++ src/libs/conduit/conduit_node.hpp | 48 ++++ src/tests/conduit/t_conduit_to_string.cpp | 296 +++++++++++++++++++ 3 files changed, 679 insertions(+) diff --git a/src/libs/conduit/conduit_node.cpp b/src/libs/conduit/conduit_node.cpp index 10a296c96..6146a882e 100644 --- a/src/libs/conduit/conduit_node.cpp +++ b/src/libs/conduit/conduit_node.cpp @@ -11981,6 +11981,341 @@ Node::to_string_default() const return to_string(); } + + +//----------------------------------------------------------------------------- +// -- Summary string construction methods --- +//----------------------------------------------------------------------------- + +//----------------------------------------------------------------------------- +std::string +Node::to_summary_string()const +{ + Node opts; + return to_summary_string(opts); +} + +//----------------------------------------------------------------------------- +std::string +Node::to_summary_string(const conduit::Node &opts)const +{ + std::ostringstream oss; + to_summary_string_stream(oss,opts); + return oss.str(); +} + +//----------------------------------------------------------------------------- +void +Node::to_summary_string_stream(std::ostream &os, + const conduit::Node &opts) const +{ + // unpack options and enforce defaults + index_t num_children_threshold = 7; + index_t num_elements_threshold = 5; + index_t indent = 2; + index_t depth = 0; + std::string pad = " "; + std::string eoe = "\n"; + + if(opts.has_child("num_children_threshold") && + opts["num_children_threshold"].dtype().is_number()) + { + num_children_threshold = (index_t)opts["num_children_threshold"].to_int32(); + } + + if(opts.has_child("num_elements_threshold") && + opts["num_elements_threshold"].dtype().is_number()) + { + num_elements_threshold = (index_t)opts["num_elements_threshold"].to_int32(); + } + + if(opts.has_child("indent") && + opts["indent"].dtype().is_number()) + { + indent = (index_t)opts["indent"].to_int32(); + } + + if(opts.has_child("depth") && + opts["depth"].dtype().is_number()) + { + depth = (index_t)opts["depth"].to_int32(); + } + + if(opts.has_child("pad") && + opts["pad"].dtype().is_string()) + { + pad = opts["pad"].as_string(); + } + + if(opts.has_child("eoe") && + opts["eoe"].dtype().is_string()) + { + eoe = opts["eoe"].as_string(); + } + + to_summary_string_stream(os, + num_children_threshold, + num_elements_threshold, + indent, + depth, + pad, + eoe); +} + + +//----------------------------------------------------------------------------- +//-- (private interface) +//----------------------------------------------------------------------------- +void +Node::to_summary_string_stream(const std::string &stream_path, + const conduit::Node &opts) const +{ + std::ofstream ofs; + ofs.open(stream_path.c_str()); + if(!ofs.is_open()) + { + CONDUIT_ERROR(" failed to open file: " + << "\"" << stream_path << "\""); + } + to_summary_string_stream(ofs,opts); + ofs.close(); +} + +//----------------------------------------------------------------------------- +std::string +Node::to_summary_string_default() const +{ + return to_summary_string(); +} + + +//----------------------------------------------------------------------------- +//-- (private interface) +//----------------------------------------------------------------------------- +void +Node::to_summary_string_stream(std::ostream &os, + index_t num_children_threshold, + index_t num_elements_threshold, + index_t indent, + index_t depth, + const std::string &pad, + const std::string &eoe) const +{ + // rubber, say hello to the road: + + std::ios_base::fmtflags prev_stream_flags(os.flags()); + os.precision(15); + if(dtype().id() == DataType::OBJECT_ID) + { + os << eoe; + int nchildren = m_children.size(); + int threshold = num_children_threshold; + + // if we are neg or zero, show all children + if(threshold <=0) + { + threshold = nchildren; + } + + // if above threshold only show threshold # of values + int half = threshold / 2; + int bottom = half; + int top = half; + int num_skipped = m_children.size() - threshold; + + // + // if odd, show 1/2 +1 first + // + + if( (threshold % 2) > 0) + { + bottom++; + } + + bool done = (nchildren == 0); + int idx = 0; + + while(!done) + { + utils::indent(os,indent,depth,pad); + os << m_schema->object_order()[idx] << ": "; + m_children[idx]->to_summary_string_stream(os, + num_children_threshold, + num_elements_threshold, + indent, + depth+1, + pad, + eoe); + + // if the child is a leaf, we need eoe + if(m_children[idx]->number_of_children() == 0) + os << eoe; + + idx++; + + if(idx == bottom && num_skipped > 0) + { + utils::indent(os,indent,depth,pad); + idx = nchildren - top; + os << "... ( skipped " + << num_skipped; + if( num_skipped == 1) + { + os << " child )"; + } + else + { + os << " children )"; + } + os << eoe; + } + + if(idx == nchildren) + { + done = true; + } + } + } + else if(dtype().id() == DataType::LIST_ID) + { + os << eoe; + int nchildren = m_children.size(); + int threshold = num_children_threshold; + + // if we are neg or zero, show all children + if(threshold <=0) + { + threshold = nchildren; + } + + // if above threshold only show threshold # of values + int half = threshold / 2; + int bottom = half; + int top = half; + int num_skipped = m_children.size() - threshold; + + // + // if odd, show 1/2 +1 first + // + + if( (threshold % 2) > 0) + { + bottom++; + } + + bool done = (nchildren == 0); + int idx = 0; + + while(!done) + { + utils::indent(os,indent,depth,pad); + os << "- "; + m_children[idx]->to_summary_string_stream(os, + num_children_threshold, + num_elements_threshold, + indent, + depth+1, + pad, + eoe); + + // if the child is a leaf, we need eoe + if(m_children[idx]->number_of_children() == 0) + os << eoe; + + idx++; + + if(idx == bottom && num_skipped > 0) + { + utils::indent(os,indent,depth,pad); + idx = nchildren - top; + os << "... ( skipped " + << num_skipped; + if( num_skipped == 1) + { + os << " child )"; + } + else + { + os << " children )"; + } + os << eoe; + } + + if(idx == nchildren) + { + done = true; + } + } + } + else // assume leaf data type + { + // if we are neg or zero, show full array + // + if(num_elements_threshold <= 0) + { + num_elements_threshold = dtype().number_of_elements(); + } + + switch(dtype().id()) + { + // ints + case DataType::INT8_ID: + as_int8_array().to_summary_string_stream(os, + num_elements_threshold); + break; + case DataType::INT16_ID: + as_int16_array().to_summary_string_stream(os, + num_elements_threshold); + break; + case DataType::INT32_ID: + as_int32_array().to_summary_string_stream(os, + num_elements_threshold); + break; + case DataType::INT64_ID: + as_int64_array().to_summary_string_stream(os, + num_elements_threshold); + break; + // uints + case DataType::UINT8_ID: + as_uint8_array().to_summary_string_stream(os, + num_elements_threshold); + break; + case DataType::UINT16_ID: + as_uint16_array().to_summary_string_stream(os, + num_elements_threshold); + break; + case DataType::UINT32_ID: + as_uint32_array().to_summary_string_stream(os, + num_elements_threshold); + break; + case DataType::UINT64_ID: + as_uint64_array().to_summary_string_stream(os, + num_elements_threshold); + break; + // floats + case DataType::FLOAT32_ID: + as_float32_array().to_summary_string_stream(os, + num_elements_threshold); + break; + case DataType::FLOAT64_ID: + as_float64_array().to_summary_string_stream(os, + num_elements_threshold); + break; + // char8_str + case DataType::CHAR8_STR_ID: + os << "\"" + << utils::escape_special_chars(as_string()) + << "\""; + break; + // empty + case DataType::EMPTY_ID: + break; + } + } + + os.flags(prev_stream_flags); +} + //----------------------------------------------------------------------------- // -- JSON construction methods --- //----------------------------------------------------------------------------- diff --git a/src/libs/conduit/conduit_node.hpp b/src/libs/conduit/conduit_node.hpp index 8308d69bc..733012c87 100644 --- a/src/libs/conduit/conduit_node.hpp +++ b/src/libs/conduit/conduit_node.hpp @@ -3508,6 +3508,42 @@ class CONDUIT_API Node // difficulty allocating default string parameters. std::string to_string_default() const; +//----------------------------------------------------------------------------- +// -- Summary string construction methods --- +//----------------------------------------------------------------------------- + /// Creates a summary string representation of a node. + /// + /// `opts` Node is used to pass formatting params. + /// + /// Supported `opts` entries: + /// + /// num_children_threshold: max number of children to print for a list or object + /// (default: 7) + /// num_elements_threshold: max number of elements to print for a leaf + /// (default: 5) + /// indent: indention amount (default: 2 instances of pad) + /// depth: indention level (default: 0) + /// pad: padding string (default: " ") + // eoe: end of element string (default: "\n") + /// + /// formatting details: + /// these methods prefix entries with indent strings created using + /// utils::indent(...,indent, depth, pad) + /// adds the `eoe` (end-of-entry) suffix where necessary. + + std::string to_summary_string() const; + std::string to_summary_string(const conduit::Node &opts) const; + void to_summary_string_stream(std::ostream &os, + const conduit::Node &opts) const; + void to_summary_string_stream(const std::string &stream_path, + const conduit::Node &opts) const; + + // NOTE(cyrush): In this case to_summary_string() is easily callable + // in debugging tools, but still provide the to_summary_string_default() + // variant to present a similar interface to other `to_` methods. + std::string to_summary_string_default() const; + + //----------------------------------------------------------------------------- // -- JSON construction methods --- //----------------------------------------------------------------------------- @@ -4476,6 +4512,18 @@ class CONDUIT_API Node const std::string &pad=" ", const std::string &eoe="\n") const; + //------------------------------------------------------------------------- + // private summary string helper + // (public interface methods bundle options in a node) + //------------------------------------------------------------------------- + void to_summary_string_stream(std::ostream &os, + index_t num_children_threshold, + index_t num_elements_threshold, + index_t indent, + index_t depth, + const std::string &pad, + const std::string &eoe) const; + //----------------------------------------------------------------------------- // // -- conduit::Node private data members -- diff --git a/src/tests/conduit/t_conduit_to_string.cpp b/src/tests/conduit/t_conduit_to_string.cpp index 016b03c11..e2e9243f1 100644 --- a/src/tests/conduit/t_conduit_to_string.cpp +++ b/src/tests/conduit/t_conduit_to_string.cpp @@ -44,3 +44,299 @@ TEST(conduit_to_string, simple_1) delete [] data; } + + +//----------------------------------------------------------------------------- +TEST(conduit_to_string, conduit_to_summary_string_obj) +{ + Node n; + n["a"] = {0,1,2,3,4,5,6,7,8,9}; + n["b"] = {1,1,2,3,4,5,6,7,8,9}; + n["c"] = {2,1,2,3,4,5,6,7,8,9}; + n["d"] = {3,1,2,3,4,5,6,7,8,9}; + n["e"] = {4,1,2,3,4,5,6,7,8,9}; + n["f"] = {5,1,2,3,4,5,6,7,8,9}; + n["g/aa"] = {0,1,2,3,4,5,6,7,8,9}; + n["g/bb"] = {1,1,2,3,4,5,6,7,8,9}; + n["g/cc"] = {2,1,2,3,4,5,6,7,8,9}; + n["g/dd"] = {3,1,2,3,4,5,6,7,8,9}; + + std::cout << "yaml rep" << std::endl; + std::cout << n.to_yaml() << std::endl; + + Node opts; + std::string tres = ""; + std::string texpect = ""; + + std::cout << "default to_summary_string" << std::endl; + tres = n.to_summary_string(); + std::cout << tres << std::endl; +// for g: yes there is an extra space here ... + texpect = R"ST( +a: [0, 1, 2, ..., 8, 9] +b: [1, 1, 2, ..., 8, 9] +c: [2, 1, 2, ..., 8, 9] +d: [3, 1, 2, ..., 8, 9] +e: [4, 1, 2, ..., 8, 9] +f: [5, 1, 2, ..., 8, 9] +g: + aa: [0, 1, 2, ..., 8, 9] + bb: [1, 1, 2, ..., 8, 9] + cc: [2, 1, 2, ..., 8, 9] + dd: [3, 1, 2, ..., 8, 9] +)ST"; + EXPECT_EQ(tres,texpect); + + opts.reset(); + opts["num_children_threshold"] = 5; + opts["num_elements_threshold"] = 4; + opts.print(); + tres = n.to_summary_string(opts); + std::cout << tres << std::endl; + +// for g: yes there is an extra space here ... + texpect = R"ST( +a: [0, 1, ..., 8, 9] +b: [1, 1, ..., 8, 9] +c: [2, 1, ..., 8, 9] +... ( skipped 2 children ) +f: [5, 1, ..., 8, 9] +g: + aa: [0, 1, ..., 8, 9] + bb: [1, 1, ..., 8, 9] + cc: [2, 1, ..., 8, 9] + dd: [3, 1, ..., 8, 9] +)ST"; + EXPECT_EQ(tres,texpect); + + opts.reset(); + opts["num_children_threshold"] = 3; + opts["num_elements_threshold"] = 4; + opts.print(); + tres = n.to_summary_string(opts); + std::cout << tres << std::endl; + + texpect = R"ST( +a: [0, 1, ..., 8, 9] +b: [1, 1, ..., 8, 9] +... ( skipped 4 children ) +g: + aa: [0, 1, ..., 8, 9] + bb: [1, 1, ..., 8, 9] + ... ( skipped 1 child ) + dd: [3, 1, ..., 8, 9] +)ST"; + EXPECT_EQ(tres,texpect); + + // neg or zero for thresholds should trigger default yaml case + opts.reset(); + opts["num_elements_threshold"] = -1; + opts["num_children_threshold"] = -1; + opts.print(); + tres = n.to_summary_string(opts); + std::cout << tres << std::endl; + EXPECT_EQ(tres,n.to_yaml()); + + // high thresholds should trigger default yaml case + opts.reset(); + opts["num_elements_threshold"] = 100; + opts["num_children_threshold"] = 100; + opts.print(); + tres = n.to_summary_string(opts); + std::cout << tres << std::endl; + EXPECT_EQ(tres,n.to_yaml()); + +} + +//----------------------------------------------------------------------------- +TEST(conduit_to_string, conduit_to_summary_string_list) +{ + Node n, opts; + std::string tres, texpect; + // list cases: + // 7 children at root + n.append().set({0,1,2,3,4,5,6,7,8,9}); + n.append().set({1,1,2,3,4,5,6,7,8,9}); + n.append().set({2,1,2,3,4,5,6,7,8,9}); + n.append().set({3,1,2,3,4,5,6,7,8,9}); + n.append().set({4,1,2,3,4,5,6,7,8,9}); + n.append().set({5,1,2,3,4,5,6,7,8,9}); + // 4 children in sub + Node &n_sub = n.append(); + n_sub.append().set({0,1,2,3,4,5,6,7,8,9}); + n_sub.append().set({1,1,2,3,4,5,6,7,8,9}); + n_sub.append().set({2,1,2,3,4,5,6,7,8,9}); + n_sub.append().set({3,1,2,3,4,5,6,7,8,9}); + + std::cout << "yaml rep" << std::endl; + std::cout << n.to_yaml() << std::endl; + + std::cout << "default to_summary_string" << std::endl; + tres = n.to_summary_string(); + std::cout << tres << std::endl; + + texpect = R"ST( +- [0, 1, 2, ..., 8, 9] +- [1, 1, 2, ..., 8, 9] +- [2, 1, 2, ..., 8, 9] +- [3, 1, 2, ..., 8, 9] +- [4, 1, 2, ..., 8, 9] +- [5, 1, 2, ..., 8, 9] +- + - [0, 1, 2, ..., 8, 9] + - [1, 1, 2, ..., 8, 9] + - [2, 1, 2, ..., 8, 9] + - [3, 1, 2, ..., 8, 9] +)ST"; + EXPECT_EQ(tres,texpect); + + opts.reset(); + opts["num_children_threshold"] = 5; + opts["num_elements_threshold"] = 4; + opts.print(); + tres = n.to_summary_string(opts); + std::cout << tres << std::endl; + + texpect = R"ST( +- [0, 1, ..., 8, 9] +- [1, 1, ..., 8, 9] +- [2, 1, ..., 8, 9] +... ( skipped 2 children ) +- [5, 1, ..., 8, 9] +- + - [0, 1, ..., 8, 9] + - [1, 1, ..., 8, 9] + - [2, 1, ..., 8, 9] + - [3, 1, ..., 8, 9] +)ST"; + EXPECT_EQ(tres,texpect); + + opts.reset(); + opts["num_children_threshold"] = 3; + opts["num_elements_threshold"] = 4; + opts.print(); + tres = n.to_summary_string(opts); + std::cout << tres << std::endl; + + texpect = R"ST( +- [0, 1, ..., 8, 9] +- [1, 1, ..., 8, 9] +... ( skipped 4 children ) +- + - [0, 1, ..., 8, 9] + - [1, 1, ..., 8, 9] + ... ( skipped 1 child ) + - [3, 1, ..., 8, 9] +)ST"; + EXPECT_EQ(tres,texpect); + + // neg or zero for thresholds should trigger default yaml case + opts.reset(); + opts["num_elements_threshold"] = -1; + opts["num_children_threshold"] = -1; + opts.print(); + tres = n.to_summary_string(opts); + std::cout << tres << std::endl; + EXPECT_EQ(tres,n.to_yaml()); + + // high thresholds should trigger default yaml case + opts.reset(); + opts["num_elements_threshold"] = 100; + opts["num_children_threshold"] = 100; + opts.print(); + tres = n.to_summary_string(opts); + std::cout << tres << std::endl; + EXPECT_EQ(tres,n.to_yaml()); +} + +//----------------------------------------------------------------------------- +TEST(conduit_to_string, conduit_to_summary_string_misc) +{ + Node n; + Node opts; + std::string tres, texpect; + // misc cases + // empty + opts.reset(); + n.reset(); + opts.print(); + n.print(); + tres = n.to_summary_string(opts); + std::cout << tres << std::endl; + EXPECT_EQ(tres,n.to_yaml()); + + // scalar + opts.reset(); + n.reset(); + n = 10; + opts.print(); + n.print(); + tres = n.to_summary_string(); + std::cout << tres << std::endl; + EXPECT_EQ(tres,n.to_yaml()); + + // small array + opts.reset(); + n.reset(); + n = {1,2,3}; + opts.print(); + n.print(); + tres = n.to_summary_string(opts); + std::cout << tres << std::endl; + EXPECT_EQ(tres,n.to_yaml()); + + opts.reset(); + n.reset(); + // + opts["num_elements_threshold"] = 2; + n = {1,2,3}; + opts.print(); + n.print(); + tres = n.to_summary_string(opts); + std::cout << tres << std::endl; + EXPECT_EQ(tres,"[1, ..., 3]"); + + // small num of children + opts.reset(); + n.reset(); + opts["num_children_threshold"] = 2; + n["a"] = 10; + n["b"] = 20; + n["c"] = 20; + + tres = n.to_summary_string(opts); + std::cout << tres << std::endl; + + texpect = R"ST( +a: 10 +... ( skipped 1 child ) +c: 20 +)ST"; + EXPECT_EQ(tres,texpect); + + + // list and obj + opts.reset(); + n.reset(); + opts["num_children_threshold"] = 2; + n["a"] = 10; + n["b"] = 20; + n["c"].append().set(10); + n["c"].append().set(20); + n["c"].append().set(30); + n["c"].append().set(40); + + tres = n.to_summary_string(opts); + std::cout << tres << std::endl; + + texpect = R"ST( +a: 10 +... ( skipped 1 child ) +c: + - 10 + ... ( skipped 2 children ) + - 40 +)ST"; + EXPECT_EQ(tres,texpect); + +} \ No newline at end of file From 8f7ee9c0adce0b47be2a4fd9591ac98f785300a7 Mon Sep 17 00:00:00 2001 From: Cyrus Harrison Date: Fri, 5 Feb 2021 12:28:52 -0800 Subject: [PATCH 2/4] update changelog --- CHANGELOG.md | 11 ++++++++--- 1 file changed, 8 insertions(+), 3 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 6b7b23b02..118a6352c 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -19,8 +19,8 @@ and this project aspires to adhere to [Semantic Versioning](https://semver.org/s - Added support for using C++11 initializer lists to set Node and DataArray values from numeric arrays. See C++ tutorial docs (https://llnl-conduit.readthedocs.io/en/latest/tutorial_cpp_numeric.html#c-11-initializer-lists) for more details. - Added a Node::describe() method. This method creates a new node that mirrors the current Node, however each leaf is replaced by summary stats and a truncated display of the values. For use cases with large leaves, printing the describe() output Node is much more helpful for debugging and understanding vs wall of text from other to_string() methods. - Added conduit::utils::format methods. These methods use fmt to format strings that include fmt style patterns. The formatting arguments are passed as a conduit::Node tree. The `args` case allows named arguments (args passed as object) or ordered args (args passed as list). The `maps` case also supports named or ordered args and works in conjunction with a `map_index`. The `map_index` is used to fetch a value from an array, or list of strings, which is then passed to fmt. The `maps` style of indexed indirection supports generating path strings for non-trivial domain partition mappings in Blueprint. This functionality is also available in Python, via the `conduit.utils.format` method. -- Added DataArray::fill method, which set all elements of a DataArray to a given value. - +- Added `DataArray::fill` method, which set all elements of a DataArray to a given value. +- Added `Node::to_summary_string` methods, which allow you to create truncated strings that describe a node tree, control the max number of children and max number of elements shown. #### Relay - Added Relay IO Handle mode support for `a` (append) and `t` (truncate). Truncate allows you to overwrite files when the handle is opened. The default is append, which preserves prior IO Handle behavior. @@ -43,8 +43,13 @@ and this project aspires to adhere to [Semantic Versioning](https://semver.org/s ### Removed +#### General +- Removed `Node::fetch_child` and `Schema::fetch_child` methods for v0.7.0. (Deprecated in v0.6.0 -- prefer `fetch_existing`) +- Removed `Schema::to_json` method variants with `detailed` for v0.7.0. (Deprecated in v0.6.0 -- prefer standard `to_json`) +- Removed `Schema::save` method variant with `detailed` for v0.7.0. (Deprecated in v0.6.0 -- prefer standard `save`) + #### Relay -- `conduit::relay::io_blueprint::save` methods were removed for v0.7.0. (Deprecated in v0.6.0) +- Removed `conduit::relay::io_blueprint::save` methods for v0.7.0. (Deprecated in v0.6.0 -- prefer `conduit::relay::io::blueprint::save_mesh`) From 860500898ef3c8d3cd9eec6aa104b5b2fb56b028 Mon Sep 17 00:00:00 2001 From: Cyrus Harrison Date: Fri, 5 Feb 2021 15:46:28 -0800 Subject: [PATCH 3/4] python support for to_summary_string --- CHANGELOG.md | 7 +++ src/libs/conduit/python/conduit_python.cpp | 62 ++++++++++++++++++- .../conduit/python/t_python_conduit_node.py | 50 +++++++++++++++ src/tests/conduit/t_conduit_to_string.cpp | 2 +- 4 files changed, 119 insertions(+), 2 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 118a6352c..e1f2ad015 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -21,6 +21,7 @@ and this project aspires to adhere to [Semantic Versioning](https://semver.org/s - Added conduit::utils::format methods. These methods use fmt to format strings that include fmt style patterns. The formatting arguments are passed as a conduit::Node tree. The `args` case allows named arguments (args passed as object) or ordered args (args passed as list). The `maps` case also supports named or ordered args and works in conjunction with a `map_index`. The `map_index` is used to fetch a value from an array, or list of strings, which is then passed to fmt. The `maps` style of indexed indirection supports generating path strings for non-trivial domain partition mappings in Blueprint. This functionality is also available in Python, via the `conduit.utils.format` method. - Added `DataArray::fill` method, which set all elements of a DataArray to a given value. - Added `Node::to_summary_string` methods, which allow you to create truncated strings that describe a node tree, control the max number of children and max number of elements shown. +- Added python support for `Node.to_summary_string` #### Relay - Added Relay IO Handle mode support for `a` (append) and `t` (truncate). Truncate allows you to overwrite files when the handle is opened. The default is append, which preserves prior IO Handle behavior. @@ -36,6 +37,12 @@ and this project aspires to adhere to [Semantic Versioning](https://semver.org/s - Added `material_map` to `conduit::blueprint::mesh:matset::index`, to provide an explicit material name to id mapping. - Added `mat_check` field to `blueprint::mesh::examples::venn`. This field encodes the material info in a scalar field and in the `matset_values` in a way that can be used to easily compare and verify proper construction in other tools. +### Changed + +#### General +- Python Node repr string construction now uses `Node.to_summary_string()` + + ### Fixed #### Relay diff --git a/src/libs/conduit/python/conduit_python.cpp b/src/libs/conduit/python/conduit_python.cpp index 932c30e0d..cb7574fca 100644 --- a/src/libs/conduit/python/conduit_python.cpp +++ b/src/libs/conduit/python/conduit_python.cpp @@ -1696,6 +1696,7 @@ PyConduit_DataType_to_string(PyConduit_DataType* self, return (Py_BuildValue("s", oss.str().c_str())); } + //---------------------------------------------------------------------------// static PyObject * PyConduit_DataType_to_json(PyConduit_DataType* self, @@ -4177,7 +4178,7 @@ PyConduit_Node_str(PyConduit_Node* self) static PyObject * PyConduit_Node_repr(PyConduit_Node* self) { - return PyConduit_Node_str(self); + return (Py_BuildValue("s", self->node->to_summary_string().c_str())); } //---------------------------------------------------------------------------// @@ -5537,6 +5538,59 @@ PyConduit_Node_to_string(PyConduit_Node* self, return (Py_BuildValue("s", oss.str().c_str())); } +//---------------------------------------------------------------------------// +static PyObject * +PyConduit_Node_to_summary_string(PyConduit_Node* self, + PyObject* args, + PyObject* kwargs) +{ + PyObject *py_opts = NULL; + static const char *kwlist[] = {"opts", + NULL}; + + if (!PyArg_ParseTupleAndKeywords(args, + kwargs, + "|O", + const_cast(kwlist), + &py_opts)) + { + return (NULL); + } + + if(py_opts != NULL && !PyConduit_Node_Check(py_opts)) + { + PyErr_SetString(PyExc_TypeError, + "'opts' argument must be a " + "conduit.Node instance"); + return NULL; + } + + Node opts; + Node *opts_ptr = &opts; + + if(py_opts != NULL) + { + opts_ptr = PyConduit_Node_Get_Node_Ptr(py_opts); + } + + std::ostringstream oss; + + try + { + self->node->to_summary_string_stream(oss, + *opts_ptr); + + } + catch(conduit::Error e) + { + PyErr_SetString(PyExc_IOError, + e.message().c_str()); + return NULL; + } + + return (Py_BuildValue("s", oss.str().c_str())); +} + //---------------------------------------------------------------------------// static PyObject * PyConduit_Node_to_json(PyConduit_Node* self, @@ -5959,6 +6013,12 @@ static PyMethodDef PyConduit_Node_METHODS[] = { "Returns a string representation of the node. " "Optionally takes protocol and spacing options. " "(Default protocol='yaml'.)"}, + //------------------------------------------------------------t-----------// + {"to_summary_string", + (PyCFunction)PyConduit_Node_to_summary_string, + METH_VARARGS| METH_KEYWORDS, + "Returns a summary string representation of the node. " + "Optionally takes a Node that provides spacing and threshold options. "}, //-----------------------------------------------------------------------// {"to_json", (PyCFunction)PyConduit_Node_to_json, diff --git a/src/tests/conduit/python/t_python_conduit_node.py b/src/tests/conduit/python/t_python_conduit_node.py index 698df74d8..4c3d29c60 100644 --- a/src/tests/conduit/python/t_python_conduit_node.py +++ b/src/tests/conduit/python/t_python_conduit_node.py @@ -577,6 +577,56 @@ def test_describe(self): d = n.describe(opts) print(d) + def test_summary_string(self): + n = Node() + n["a"] = [1,2,3,4,5]; + n["b"] = [1,2,3]; + n["c"] = [1,2,3,4,5,6]; + n["d"] = [1,2,3,4,5,6,7]; + n["e"] = [1,2,3,4,5,6,7,8,9,10,11,12]; + n["f"] = [1.0,2.0,3.0,4.0,5.0,6.0,7.0]; + n["g"] = [2.0,4.0]; + + print(repr(n)) + + r = n.to_summary_string() + print(r) + texp = """ +a: [1, 2, 3, 4, 5] +b: [1, 2, 3] +c: [1, 2, 3, ..., 5, 6] +d: [1, 2, 3, ..., 6, 7] +e: [1, 2, 3, ..., 11, 12] +f: [1.0, 2.0, 3.0, ..., 6.0, 7.0] +g: [2.0, 4.0] +""" + self.assertEqual(r,texp) + + opts = Node() + opts["num_children_threshold"] = 2 + opts["num_elements_threshold"] = 3 + r = n.to_summary_string(opts) + print(r) + + texp = """ +a: [1, 2, ..., 5] +... ( skipped 5 children ) +g: [2.0, 4.0] +""" + self.assertEqual(r,texp) + r = n.to_summary_string(opts=opts) + print(r) + + self.assertEqual(r,texp) + + opts = Node() + opts["num_children_threshold"] = 100 + opts["num_elements_threshold"] = -1 + r = n.to_summary_string(opts) + print(r) + + self.assertEqual(r,n.to_yaml()) + if __name__ == '__main__': unittest.main() diff --git a/src/tests/conduit/t_conduit_to_string.cpp b/src/tests/conduit/t_conduit_to_string.cpp index e2e9243f1..d5914c12f 100644 --- a/src/tests/conduit/t_conduit_to_string.cpp +++ b/src/tests/conduit/t_conduit_to_string.cpp @@ -339,4 +339,4 @@ a: 10 )ST"; EXPECT_EQ(tres,texpect); -} \ No newline at end of file +} From c3feda920bae7dd70d949dba4ce097feece9ad46 Mon Sep 17 00:00:00 2001 From: Cyrus Harrison Date: Fri, 5 Feb 2021 15:49:18 -0800 Subject: [PATCH 4/4] small py3 fix for helper --- package.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/package.py b/package.py index 3e3bc6167..77eced68d 100755 --- a/package.py +++ b/package.py @@ -29,7 +29,7 @@ def create_package(output_file,version): if not version is None: cmd += "-" + version cmd += " " + output_file - print "[exe: %s]" % cmd + print("[exe: %s]" % cmd) subprocess.call(cmd,shell=True) if __name__ == "__main__":