diff --git a/api/python/ryml/tests/test_parse.py b/api/python/ryml/tests/test_parse.py index a3d2b36f..85c12189 100644 --- a/api/python/ryml/tests/test_parse.py +++ b/api/python/ryml/tests/test_parse.py @@ -158,7 +158,7 @@ class SimpleHardcoded: yaml = "{'HELLO': a, foo: \"b\", bar: c, baz: d, seq: [0, 1, 2, 3]}" - def check(self, ut, t): + def check(self, ut, t, is_json=False): # some convenient shorthands eq = ut.assertEqual ne = ut.assertNotEqual @@ -209,29 +209,58 @@ class SimpleHardcoded: eq(t.val(7), b"1") eq(t.val(8), b"2") eq(t.val(9), b"3") - tr(t.is_key_quoted(1)) - fs(t.is_key_quoted(2)) - fs(t.is_key_quoted(3)) - fs(t.is_key_quoted(4)) - fs(t.is_key_quoted(5)) - fs(t.is_val_quoted(1)) - tr(t.is_val_quoted(2)) - fs(t.is_val_quoted(3)) - fs(t.is_val_quoted(4)) - fs(t.is_val_quoted(5)) - fs(t.is_val_quoted(6)) - fs(t.is_val_quoted(7)) - fs(t.is_val_quoted(8)) - fs(t.is_val_quoted(9)) - tr(t.is_quoted(1)) - tr(t.is_quoted(2)) - fs(t.is_quoted(3)) - fs(t.is_quoted(4)) - fs(t.is_quoted(5)) - fs(t.is_quoted(6)) - fs(t.is_quoted(7)) - fs(t.is_quoted(8)) - fs(t.is_quoted(9)) + if not is_json: + tr(t.is_key_quoted(1)) + fs(t.is_key_quoted(2)) + fs(t.is_key_quoted(3)) + fs(t.is_key_quoted(4)) + fs(t.is_key_quoted(5)) + else: + tr(t.is_key_quoted(1)) + tr(t.is_key_quoted(2)) + tr(t.is_key_quoted(3)) + tr(t.is_key_quoted(4)) + tr(t.is_key_quoted(5)) + if not is_json: + fs(t.is_val_quoted(1)) + tr(t.is_val_quoted(2)) + fs(t.is_val_quoted(3)) + fs(t.is_val_quoted(4)) + fs(t.is_val_quoted(5)) + fs(t.is_val_quoted(6)) + fs(t.is_val_quoted(7)) + fs(t.is_val_quoted(8)) + fs(t.is_val_quoted(9)) + else: + tr(t.is_val_quoted(1)) + tr(t.is_val_quoted(2)) + tr(t.is_val_quoted(3)) + tr(t.is_val_quoted(4)) + fs(t.is_val_quoted(5)) + fs(t.is_val_quoted(6)) + fs(t.is_val_quoted(7)) + fs(t.is_val_quoted(8)) + fs(t.is_val_quoted(9)) + if not is_json: + tr(t.is_quoted(1)) + tr(t.is_quoted(2)) + fs(t.is_quoted(3)) + fs(t.is_quoted(4)) + fs(t.is_quoted(5)) + fs(t.is_quoted(6)) + fs(t.is_quoted(7)) + fs(t.is_quoted(8)) + fs(t.is_quoted(9)) + else: + tr(t.is_quoted(1)) + tr(t.is_quoted(2)) + tr(t.is_quoted(3)) + tr(t.is_quoted(4)) + tr(t.is_quoted(5)) + fs(t.is_quoted(6)) + fs(t.is_quoted(7)) + fs(t.is_quoted(8)) + fs(t.is_quoted(9)) tr(t.has_sibling(1, b"bar")) tr(t.has_sibling(1, b"baz")) tr(t.has_sibling(2, b"foo")) @@ -385,35 +414,65 @@ class _TestBase(unittest.TestCase): self.case.check(self, t) # ---------------------------------------------------------- - def _test41_emit(self): + def _test41_emit_yaml(self): tree = ryml.parse_in_arena(self.src_as_bytearray) - yaml = ryml.emit(tree) + yaml = ryml.emit_yaml(tree) output_tree = ryml.parse_in_arena(yaml) self.case.check(self, output_tree) - def _test42_compute_emit_length(self): + def _test41_emit_json(self): tree = ryml.parse_in_arena(self.src_as_bytearray) - yaml = ryml.emit(tree) - length = ryml.compute_emit_length(tree) + json = ryml.emit_json(tree) + output_tree = ryml.parse_in_arena(json) + self.case.check(self, output_tree, is_json=True) + + def _test42_compute_emit_yaml_length(self): + tree = ryml.parse_in_arena(self.src_as_bytearray) + yaml = ryml.emit_yaml(tree) + length = ryml.compute_emit_yaml_length(tree) self.assertEqual(len(yaml), length) - def _test43_emit_in_place(self): + def _test42_compute_emit_json_length(self): tree = ryml.parse_in_arena(self.src_as_bytearray) - yaml = ryml.emit(tree) - length = ryml.compute_emit_length(tree) + json = ryml.emit_json(tree) + length = ryml.compute_emit_json_length(tree) + self.assertEqual(len(json), length) + + def _test43_emit_yaml_in_place(self): + tree = ryml.parse_in_arena(self.src_as_bytearray) + yaml = ryml.emit_yaml(tree) + length = ryml.compute_emit_yaml_length(tree) self.assertEqual(len(yaml), length) buf = bytearray(length) - s = ryml.emit_in_place(tree, buf) + s = ryml.emit_yaml_in_place(tree, buf) self.assertEqual(len(s), length) self.assertTrue(s.tobytes().decode('utf-8') == yaml) self.assertTrue(buf.decode('utf-8') == yaml) - def _test44_short_buf(self): + def _test43_emit_json_in_place(self): tree = ryml.parse_in_arena(self.src_as_bytearray) - length = ryml.compute_emit_length(tree) + json = ryml.emit_json(tree) + length = ryml.compute_emit_json_length(tree) + self.assertEqual(len(json), length) + buf = bytearray(length) + s = ryml.emit_json_in_place(tree, buf) + self.assertEqual(len(s), length) + self.assertTrue(s.tobytes().decode('utf-8') == json) + self.assertTrue(buf.decode('utf-8') == json) + + def _test44_emit_yaml_short_buf(self): + tree = ryml.parse_in_arena(self.src_as_bytearray) + length = ryml.compute_emit_yaml_length(tree) buf = bytearray(length-1) with self.assertRaises(IndexError): - ryml.emit_in_place(tree, buf) + ryml.emit_yaml_in_place(tree, buf) + + def _test44_emit_json_short_buf(self): + tree = ryml.parse_in_arena(self.src_as_bytearray) + length = ryml.compute_emit_json_length(tree) + buf = bytearray(length-1) + with self.assertRaises(IndexError): + ryml.emit_json_in_place(tree, buf) # ----------------------------------------------------------------------------- @@ -457,17 +516,30 @@ class TestSimpleHardCoded(_TestBase): def test34_bytearray__rw__reuse_tree(self): self._test34_bytearray__rw__reuse_tree() - def test41_emit(self): - self._test41_emit() + # ---------------------------------------------------------- + def test41_emit_yaml(self): + self._test41_emit_yaml() - def test42_compute_emit_length(self): - self._test42_compute_emit_length() + def test41_emit_json(self): + self._test41_emit_json() - def test43_emit_in_place(self): - self._test43_emit_in_place() + def test42_compute_emit_yaml_length(self): + self._test42_compute_emit_yaml_length() - def test44_short_buf(self): - self._test44_short_buf() + def test42_compute_emit_json_length(self): + self._test42_compute_emit_json_length() + + def test43_emit_yaml_in_place(self): + self._test43_emit_yaml_in_place() + + def test43_emit_json_in_place(self): + self._test43_emit_json_in_place() + + def test44_emit_yaml_short_buf(self): + self._test44_emit_yaml_short_buf() + + def test44_emit_json_short_buf(self): + self._test44_emit_json_short_buf() # ----------------------------------------------------------------------------- diff --git a/api/ryml.i b/api/ryml.i index 1069fd07..78b2de2d 100644 --- a/api/ryml.i +++ b/api/ryml.i @@ -123,31 +123,60 @@ void parse_substr(c4::substr s, c4::yml::Tree *t) c4::yml::parse_in_place(s, t); } -char * emit_malloc(const c4::yml::Tree &t, size_t id) +char * emit_yaml_malloc(c4::yml::Tree const& t, size_t id) { c4::substr buf; - c4::substr ret = c4::yml::emit(t, id, buf, /*error_on_excess*/false); + c4::substr ret = c4::yml::emit_yaml(t, id, buf, /*error_on_excess*/false); if(ret.str == nullptr && ret.len > 0) { // Use new[] to parse with delete[] in SWIG. - char * alloc = new char[ret.len+1]; + char * alloc = new char[ret.len + 1]; // we'll return a c-string and not a csubstr c4::substr alloced_buf(alloc, ret.len); - ret = c4::yml::emit(t, id, alloced_buf, /*error_on_excess*/true); + ret = c4::yml::emit_yaml(t, id, alloced_buf, /*error_on_excess*/true); ret.str[ret.len] = 0; } return ret.str; } -size_t emit_length(const c4::yml::Tree &t, size_t id) +char * emit_json_malloc(c4::yml::Tree const& t, size_t id) { c4::substr buf; - c4::substr ret = c4::yml::emit(t, id, buf, /*error_on_excess*/false); + c4::substr ret = c4::yml::emit_json(t, id, buf, /*error_on_excess*/false); + if(ret.str == nullptr && ret.len > 0) + { + // Use new[] to parse with delete[] in SWIG. + char * alloc = new char[ret.len + 1]; // we'll return a c-string and not a csubstr + c4::substr alloced_buf(alloc, ret.len); + ret = c4::yml::emit_json(t, id, alloced_buf, /*error_on_excess*/true); + ret.str[ret.len] = 0; + } + return ret.str; +} + +size_t emit_yaml_length(const c4::yml::Tree &t, size_t id) +{ + c4::substr buf; + c4::substr ret = c4::yml::emit_yaml(t, id, buf, /*error_on_excess*/false); return ret.len; } -bool emit_to_substr(const c4::yml::Tree &t, size_t id, c4::substr s, size_t *OUTPUT) +size_t emit_json_length(const c4::yml::Tree &t, size_t id) { - c4::substr result = c4::yml::emit(t, id, s, /*error_on_excess*/false); + c4::substr buf; + c4::substr ret = c4::yml::emit_json(t, id, buf, /*error_on_excess*/false); + return ret.len; +} + +bool emit_yaml_to_substr(const c4::yml::Tree &t, size_t id, c4::substr s, size_t *OUTPUT) +{ + c4::substr result = c4::yml::emit_yaml(t, id, s, /*error_on_excess*/false); + *OUTPUT = result.len; + return result.str == nullptr; +} + +bool emit_json_to_substr(const c4::yml::Tree &t, size_t id, c4::substr s, size_t *OUTPUT) +{ + c4::substr result = c4::yml::emit_json(t, id, s, /*error_on_excess*/false); *OUTPUT = result.len; return result.str == nullptr; } @@ -186,6 +215,8 @@ bool _same_mem(c4::csubstr l, c4::csubstr r) %pythoncode %{ +from deprecation import deprecated + def as_csubstr(s): return _get_as_csubstr(s) @@ -199,20 +230,22 @@ def u(memview): def children(tree, node=None): assert tree is not None - if node is None: node = tree.root_id() + if node is None: + node = tree.root_id() ch = tree.first_child(node) while ch != NONE: - yield ch - ch = tree.next_sibling(ch) + yield ch + ch = tree.next_sibling(ch) def siblings(tree, node): assert tree is not None - if node is None: return + if node is None: + return ch = tree.first_sibling(node) while ch != NONE: - yield ch - ch = tree.next_sibling(ch) + yield ch + ch = tree.next_sibling(ch) def walk(tree, node=None, indentation_level=0): @@ -235,28 +268,6 @@ def parse_in_arena(buf, **kwargs): return _call_parse(parse_csubstr, buf, **kwargs) -def emit(tree, id=None): - if id is None: - id = tree.root_id() - return emit_malloc(tree, id) - - -def compute_emit_length(tree, id=None): - if id is None: - id = tree.root_id() - return emit_length(tree, id) - - -def emit_in_place(tree, buf, id=None): - if id is None: - id = tree.root_id() - (failed, expected_size) = emit_to_substr(tree, id, buf) - if failed: - raise IndexError("Output buffer has {} bytes, but emit required {} bytes".format( - len(buf), expected_size)) - return memoryview(buf)[:expected_size] - - def _call_parse(parse_fn, buf, **kwargs): tree = kwargs.get("tree", Tree()) parse_fn(buf, tree) @@ -267,6 +278,50 @@ def _check_valid_for_in_situ(obj): if type(obj) in (str, bytes): raise TypeError("cannot parse in situ: " + type(obj).__name__) + + +@deprecated(deprecated_in="0.5.0", details="Use emit_yaml() instead") +def emit(tree, id=None): + return emit_yaml(tree, id) +def emit_yaml(tree, id=None): + if id is None: + id = tree.root_id() + return emit_yaml_malloc(tree, id) +def emit_json(tree, id=None): + if id is None: + id = tree.root_id() + return emit_json_malloc(tree, id) + + +@deprecated(deprecated_in="0.5.0", details="Use compute_emit_yaml_length() instead") +def compute_emit_length(tree, id=None): + return compute_emit_yaml_length(tree, id) +def compute_emit_yaml_length(tree, id=None): + if id is None: + id = tree.root_id() + return emit_yaml_length(tree, id) +def compute_emit_json_length(tree, id=None): + if id is None: + id = tree.root_id() + return emit_json_length(tree, id) + + +@deprecated(deprecated_in="0.5.0", details="Use emit_yaml_in_place() instead") +def emit_in_place(tree, buf, id=None): + return emit_yaml_in_place(tree, buf, id) +def emit_yaml_in_place(tree, buf, id=None): + return _emit_fn_in_place(tree, buf, id, emit_yaml_to_substr) +def emit_json_in_place(tree, buf, id=None): + return _emit_fn_in_place(tree, buf, id, emit_json_to_substr) +def _emit_fn_in_place(tree, buf, id, fn): + if id is None: + id = tree.root_id() + (failed, expected_size) = fn(tree, id, buf) + if failed: + raise IndexError("Output buffer has {} bytes, but emit requires {} bytes".format( + len(buf), expected_size)) + return memoryview(buf)[:expected_size] + %} //----------------------------------------------------------------------------- diff --git a/changelog/0.5.0.md b/changelog/0.5.0.md index 71e69558..b550db90 100644 --- a/changelog/0.5.0.md +++ b/changelog/0.5.0.md @@ -29,6 +29,11 @@ - The initial version of `ConstNodeRef/NodeRef` had the problem that const methods in the CRTP base did not participate in overload resolution ([#294](https://github.com/biojppm/rapidyaml/issues/294)), preventing calls from `const NodeRef` objects. This was fixed by moving non-const methods to the CRTP base and disabling them with SFINAE ([PR#295](https://github.com/biojppm/rapidyaml/pull/295)). - Also added disambiguation iteration methods: `.cbegin()`, `.cend()`, `.cchildren()`, `.csiblings()` ([PR#295](https://github.com/biojppm/rapidyaml/pull/295)). - Deprecate `emit()` and `emitrs()` ([#120](https://github.com/biojppm/rapidyaml/issues/120), [PR#303](https://github.com/biojppm/rapidyaml/pull/303)): use `emit_yaml()` and `emitrs_yaml()` instead. This was done to improve compatibility with Qt, which leaks a macro named `emit`. For more information, see [#120](https://github.com/biojppm/rapidyaml/issues/120). + - In the Python API: + - Deprecate `emit()`, add `emit_yaml()` and `emit_json()`. + - Deprecate `compute_emit_length()`, add `compute_emit_yaml_length()` and `compute_emit_json_length()`. + - Deprecate `emit_in_place()`, add `emit_yaml_in_place()` and `emit_json_in_place()`. + - Calling the deprecated functions will now trigger a warning. ### Performance improvements diff --git a/requirements.txt b/requirements.txt index 13965d81..815f8c39 100644 --- a/requirements.txt +++ b/requirements.txt @@ -5,3 +5,4 @@ build twine setuptools_scm setuptools-git +deprecation