Script 'mail_helper' called by obssrc Hello community, here is the log from the commit of package python-ruamel.yaml for openSUSE:Factory checked in at 2023-06-04 00:11:44 ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ Comparing /work/SRC/openSUSE:Factory/python-ruamel.yaml (Old) and /work/SRC/openSUSE:Factory/.python-ruamel.yaml.new.15902 (New) ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Package is "python-ruamel.yaml" Sun Jun 4 00:11:44 2023 rev:34 rq:1090264 version:0.17.31 Changes: -------- --- /work/SRC/openSUSE:Factory/python-ruamel.yaml/python-ruamel.yaml.changes 2023-05-19 11:54:51.295034300 +0200 +++ /work/SRC/openSUSE:Factory/.python-ruamel.yaml.new.15902/python-ruamel.yaml.changes 2023-06-04 00:11:45.537231422 +0200 @@ -1,0 +2,27 @@ +Thu Jun 1 05:46:10 UTC 2023 - Johannes Kastl <[email protected]> + +- update to 0.17.31: + * added tag.setter on `ScalarEvent` and on `Node`, that takes + either a `Tag` instance, or a str (reported by Sorin Sbarnea) +- update to 0.17.30: + * fix issue 467, caused by Tag instances not being hashable + (reported by Douglas Raillard) +- update to 0.17.29: + * changed the internals of the tag property from a string to a + class which allows for preservation of the original handle and + suffix. This should result in better results using documents + with %TAG directives, as well as preserving URI escapes in tag + suffixes. +- update to 0.17.28: + * fix for issue 464: documents ending with document end marker + without final newline fail to load (reported by Mariusz + Rusiniak) +- update to 0.17.27: + * fix issue with inline mappings as value for merge keys + (reported by Sirish) + * fix for 468, error inserting after accessing merge attribute on + ``CommentedMap`` (reported by Bastien gerard) + * fix for issue 461 pop + insert on same `CommentedMap` key + throwing error (reported by `John Thorvald Wodder II) + +------------------------------------------------------------------- Old: ---- ruamel.yaml-0.17.26.tar.gz New: ---- ruamel.yaml-0.17.31.tar.gz ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ Other differences: ------------------ ++++++ python-ruamel.yaml.spec ++++++ --- /var/tmp/diff_new_pack.iJFKEp/_old 2023-06-04 00:11:46.153235104 +0200 +++ /var/tmp/diff_new_pack.iJFKEp/_new 2023-06-04 00:11:46.157235129 +0200 @@ -18,7 +18,7 @@ %{?sle15_python_module_pythons} Name: python-ruamel.yaml -Version: 0.17.26 +Version: 0.17.31 Release: 0 Summary: Python YAML parser License: MIT ++++++ ruamel.yaml-0.17.26.tar.gz -> ruamel.yaml-0.17.31.tar.gz ++++++ diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/ruamel.yaml-0.17.26/CHANGES new/ruamel.yaml-0.17.31/CHANGES --- old/ruamel.yaml-0.17.26/CHANGES 2023-05-09 21:59:31.000000000 +0200 +++ new/ruamel.yaml-0.17.31/CHANGES 2023-05-31 07:56:28.000000000 +0200 @@ -1,3 +1,31 @@ +[0, 17, 31]: 2023-05-31 + - added tag.setter on `ScalarEvent` and on `Node`, that takes either + a `Tag` instance, or a str + (reported by `Sorin Sbarnea <https://sourceforge.net/u/ssbarnea/profile/>`__) + +[0, 17, 30]: 2023-05-30 + - fix issue 467, caused by Tag instances not being hashable (reported by + `Douglas Raillard + <https://bitbucket.org/%7Bcf052d92-a278-4339-9aa8-de41923bb556%7D/>`__) + +[0, 17, 29]: 2023-05-30 + - changed the internals of the tag property from a string to a class which allows + for preservation of the original handle and suffix. This should + result in better results using documents with %TAG directives, as well + as preserving URI escapes in tag suffixes. + +[0, 17, 28]: 2023-05-26 + - fix for issue 464: documents ending with document end marker without final newline + fail to load (reported by `Mariusz Rusiniak <https://sourceforge.net/u/r2dan/profile/>`__) + +[0, 17, 27]: 2023-05-25 + - fix issue with inline mappings as value for merge keys + (reported by Sirish on `StackOverflow <https://stackoverflow.com/q/76331049/1307905>`__) + - fix for 468, error inserting after accessing merge attribute on ``CommentedMap`` + (reported by `Bastien gerard <https://sourceforge.net/u/bagerard/>`__) + - fix for issue 461 pop + insert on same `CommentedMap` key throwing error + (reported by `John Thorvald Wodder II <https://sourceforge.net/u/jwodder/profile/>`__) + [0, 17, 26]: 2023-05-09 - Fix for error on edge cage for issue 459 diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/ruamel.yaml-0.17.26/PKG-INFO new/ruamel.yaml-0.17.31/PKG-INFO --- old/ruamel.yaml-0.17.26/PKG-INFO 2023-05-09 22:00:46.626469100 +0200 +++ new/ruamel.yaml-0.17.31/PKG-INFO 2023-05-31 07:58:00.526932700 +0200 @@ -1,6 +1,6 @@ Metadata-Version: 2.1 Name: ruamel.yaml -Version: 0.17.26 +Version: 0.17.31 Summary: ruamel.yaml is a YAML parser/emitter that supports roundtrip preservation of comments, seq/map flow style, and map key order Home-page: https://sourceforge.net/p/ruamel-yaml/code/ci/default/tree Author: Anthon van der Neut @@ -34,8 +34,8 @@ ``ruamel.yaml`` is a YAML 1.2 loader/dumper package for Python. -:version: 0.17.26 -:updated: 2023-05-09 +:version: 0.17.31 +:updated: 2023-05-31 :documentation: http://yaml.readthedocs.io :repository: https://sourceforge.net/projects/ruamel-yaml/ :pypi: https://pypi.org/project/ruamel.yaml/ @@ -91,8 +91,36 @@ .. should insert NEXT: at the beginning of line for next key (with empty line) +0.17.31 (2023-05-31): + - added tag.setter on `ScalarEvent` and on `Node`, that takes either + a `Tag` instance, or a str + (reported by `Sorin Sbarnea <https://sourceforge.net/u/ssbarnea/profile/>`__) + +0.17.30 (2023-05-30): + - fix issue 467, caused by Tag instances not being hashable (reported by + `Douglas Raillard + <https://bitbucket.org/%7Bcf052d92-a278-4339-9aa8-de41923bb556%7D/>`__) + +0.17.29 (2023-05-30): + - changed the internals of the tag property from a string to a class which allows + for preservation of the original handle and suffix. This should + result in better results using documents with %TAG directives, as well + as preserving URI escapes in tag suffixes. + +0.17.28 (2023-05-26): + - fix for issue 464: documents ending with document end marker without final newline + fail to load (reported by `Mariusz Rusiniak <https://sourceforge.net/u/r2dan/profile/>`__) + +0.17.27 (2023-05-25): + - fix issue with inline mappings as value for merge keys + (reported by Sirish on `StackOverflow <https://stackoverflow.com/q/76331049/1307905>`__) + - fix for 468, error inserting after accessing merge attribute on ``CommentedMap`` + (reported by `Bastien gerard <https://sourceforge.net/u/bagerard/>`__) + - fix for issue 461 pop + insert on same `CommentedMap` key throwing error + (reported by `John Thorvald Wodder II <https://sourceforge.net/u/jwodder/profile/>`__) + 0.17.26 (2023-05-09): - - Fix for error on edge cage for issue 459 + - fix for error on edge cage for issue 459 0.17.25 (2023-05-09): - fix for regression while dumping wrapped strings with too many backslashes removed @@ -188,7 +216,7 @@ attrs with `@attr.s()` (both reported by `ssph <https://sourceforge.net/u/sph/>`__) 0.17.11 (2021-08-19): - - fix error baseclass for ``DuplicateKeyErorr`` (reported by `Åukasz Rogalski + - fix error baseclass for ``DuplicateKeyError`` (reported by `Åukasz Rogalski <https://sourceforge.net/u/lrogalski/>`__) - fix typo in reader error message, causing `KeyError` during reader error (reported by `MTU <https://sourceforge.net/u/mtu/>`__) diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/ruamel.yaml-0.17.26/README.rst new/ruamel.yaml-0.17.31/README.rst --- old/ruamel.yaml-0.17.26/README.rst 2023-05-09 21:59:31.000000000 +0200 +++ new/ruamel.yaml-0.17.31/README.rst 2023-05-31 07:56:28.000000000 +0200 @@ -4,8 +4,8 @@ ``ruamel.yaml`` is a YAML 1.2 loader/dumper package for Python. -:version: 0.17.26 -:updated: 2023-05-09 +:version: 0.17.31 +:updated: 2023-05-31 :documentation: http://yaml.readthedocs.io :repository: https://sourceforge.net/projects/ruamel-yaml/ :pypi: https://pypi.org/project/ruamel.yaml/ @@ -61,8 +61,36 @@ .. should insert NEXT: at the beginning of line for next key (with empty line) +0.17.31 (2023-05-31): + - added tag.setter on `ScalarEvent` and on `Node`, that takes either + a `Tag` instance, or a str + (reported by `Sorin Sbarnea <https://sourceforge.net/u/ssbarnea/profile/>`__) + +0.17.30 (2023-05-30): + - fix issue 467, caused by Tag instances not being hashable (reported by + `Douglas Raillard + <https://bitbucket.org/%7Bcf052d92-a278-4339-9aa8-de41923bb556%7D/>`__) + +0.17.29 (2023-05-30): + - changed the internals of the tag property from a string to a class which allows + for preservation of the original handle and suffix. This should + result in better results using documents with %TAG directives, as well + as preserving URI escapes in tag suffixes. + +0.17.28 (2023-05-26): + - fix for issue 464: documents ending with document end marker without final newline + fail to load (reported by `Mariusz Rusiniak <https://sourceforge.net/u/r2dan/profile/>`__) + +0.17.27 (2023-05-25): + - fix issue with inline mappings as value for merge keys + (reported by Sirish on `StackOverflow <https://stackoverflow.com/q/76331049/1307905>`__) + - fix for 468, error inserting after accessing merge attribute on ``CommentedMap`` + (reported by `Bastien gerard <https://sourceforge.net/u/bagerard/>`__) + - fix for issue 461 pop + insert on same `CommentedMap` key throwing error + (reported by `John Thorvald Wodder II <https://sourceforge.net/u/jwodder/profile/>`__) + 0.17.26 (2023-05-09): - - Fix for error on edge cage for issue 459 + - fix for error on edge cage for issue 459 0.17.25 (2023-05-09): - fix for regression while dumping wrapped strings with too many backslashes removed @@ -158,7 +186,7 @@ attrs with `@attr.s()` (both reported by `ssph <https://sourceforge.net/u/sph/>`__) 0.17.11 (2021-08-19): - - fix error baseclass for ``DuplicateKeyErorr`` (reported by `Åukasz Rogalski + - fix error baseclass for ``DuplicateKeyError`` (reported by `Åukasz Rogalski <https://sourceforge.net/u/lrogalski/>`__) - fix typo in reader error message, causing `KeyError` during reader error (reported by `MTU <https://sourceforge.net/u/mtu/>`__) diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/ruamel.yaml-0.17.26/__init__.py new/ruamel.yaml-0.17.31/__init__.py --- old/ruamel.yaml-0.17.26/__init__.py 2023-05-09 21:59:45.000000000 +0200 +++ new/ruamel.yaml-0.17.31/__init__.py 2023-05-31 07:56:46.000000000 +0200 @@ -5,9 +5,9 @@ _package_data = dict( full_package_name='ruamel.yaml', - version_info=(0, 17, 26), - __version__='0.17.26', - version_timestamp='2023-05-09 21:59:45', + version_info=(0, 17, 31), + __version__='0.17.31', + version_timestamp='2023-05-31 07:56:46', author='Anthon van der Neut', author_email='[email protected]', description='ruamel.yaml is a YAML parser/emitter that supports roundtrip preservation of comments, seq/map flow style, and map key order', # NOQA diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/ruamel.yaml-0.17.26/comments.py new/ruamel.yaml-0.17.31/comments.py --- old/ruamel.yaml-0.17.26/comments.py 2023-05-06 09:57:34.000000000 +0200 +++ new/ruamel.yaml-0.17.31/comments.py 2023-05-30 07:31:24.000000000 +0200 @@ -14,6 +14,7 @@ from ruamel.yaml.compat import MutableSliceableSequence, nprintf # NOQA from ruamel.yaml.scalarstring import ScalarString from ruamel.yaml.anchor import Anchor +from ruamel.yaml.tag import Tag from collections.abc import MutableSet, Sized, Set, Mapping @@ -79,7 +80,6 @@ format_attrib = '_yaml_format' line_col_attrib = '_yaml_line_col' merge_attrib = '_yaml_merge' -tag_attrib = '_yaml_tag' class Comment: @@ -194,8 +194,8 @@ # to distinguish key from None -def NoComment() -> None: - pass +class NotNone: + pass # NOQA class Format: @@ -264,19 +264,6 @@ return f'LineCol({self.line}, {self.col})' -class Tag: - """store tag information for roundtripping""" - - __slots__ = ('value',) - attrib = tag_attrib - - def __init__(self) -> None: - self.value = None - - def __repr__(self) -> Any: - return f'{self.__class__.__name__}({self.value!r})' - - class CommentedBase: @property def ca(self): @@ -380,7 +367,7 @@ return getattr(self, Format.attrib) def yaml_add_eol_comment( - self, comment: Any, key: Optional[Any] = NoComment, column: Optional[Any] = None + self, comment: Any, key: Optional[Any] = NotNone, column: Optional[Any] = None ) -> None: """ there is a problem as eol comments should start with ' #' @@ -442,8 +429,8 @@ setattr(self, Tag.attrib, Tag()) return getattr(self, Tag.attrib) - def yaml_set_tag(self, value: Any) -> None: - self.tag.value = value + def yaml_set_ctag(self, value: Tag) -> None: + setattr(self, Tag.attrib, value) def copy_attributes(self, t: Any, memo: Any = None) -> None: # fmt: off @@ -511,8 +498,8 @@ def __eq__(self, other: Any) -> bool: return list.__eq__(self, other) - def _yaml_add_comment(self, comment: Any, key: Optional[Any] = NoComment) -> None: - if key is not NoComment: + def _yaml_add_comment(self, comment: Any, key: Optional[Any] = NotNone) -> None: + if key is not NotNone: self.yaml_key_comment_extend(key, comment) else: self.ca.comment = comment @@ -593,8 +580,8 @@ class CommentedKeySeq(tuple, CommentedBase): # type: ignore """This primarily exists to be able to roundtrip keys that are sequences""" - def _yaml_add_comment(self, comment: Any, key: Optional[Any] = NoComment) -> None: - if key is not NoComment: + def _yaml_add_comment(self, comment: Any, key: Optional[Any] = NotNone) -> None: + if key is not NotNone: self.yaml_key_comment_extend(key, comment) else: self.ca.comment = comment @@ -714,13 +701,13 @@ ordereddict.__init__(self, *args, **kw) def _yaml_add_comment( - self, comment: Any, key: Optional[Any] = NoComment, value: Optional[Any] = NoComment + self, comment: Any, key: Optional[Any] = NotNone, value: Optional[Any] = NotNone ) -> None: """values is set to key to indicate a value attachment of comment""" - if key is not NoComment: + if key is not NotNone: self.yaml_key_comment_extend(key, comment) return - if value is not NoComment: + if value is not NotNone: self.yaml_value_comment_extend(value, comment) else: self.ca.comment = comment @@ -799,8 +786,11 @@ if key in self._ok: del self[key] keys = [k for k in self.keys() if k in self._ok] - ma0 = getattr(self, merge_attrib, [[-1]])[0] - merge_pos = ma0[0] + try: + ma0 = getattr(self, merge_attrib, [[-1]])[0] + merge_pos = ma0[0] + except IndexError: + merge_pos = -1 if merge_pos >= 0: if merge_pos >= pos: getattr(self, merge_attrib)[0] = (merge_pos + 1, ma0[1]) @@ -920,6 +910,16 @@ for x in ordereddict.__iter__(self): yield x + def pop(self, key: Any, default: Any = NotNone) -> Any: + try: + result = self[key] + except KeyError: + if default is NotNone: + raise + return default + del self[key] + return result + def _keys(self) -> Any: for x in ordereddict.__iter__(self): yield x @@ -1030,8 +1030,8 @@ def fromkeys(keys: Any, v: Any = None) -> Any: return CommentedKeyMap(dict.fromkeys(keys, v)) - def _yaml_add_comment(self, comment: Any, key: Optional[Any] = NoComment) -> None: - if key is not NoComment: + def _yaml_add_comment(self, comment: Any, key: Optional[Any] = NotNone) -> None: + if key is not NotNone: self.yaml_key_comment_extend(key, comment) else: self.ca.comment = comment @@ -1085,13 +1085,13 @@ self |= values def _yaml_add_comment( - self, comment: Any, key: Optional[Any] = NoComment, value: Optional[Any] = NoComment + self, comment: Any, key: Optional[Any] = NotNone, value: Optional[Any] = NotNone ) -> None: """values is set to key to indicate a value attachment of comment""" - if key is not NoComment: + if key is not NotNone: self.yaml_key_comment_extend(key, comment) return - if value is not NoComment: + if value is not NotNone: self.yaml_value_comment_extend(value, comment) else: self.ca.comment = comment @@ -1128,7 +1128,9 @@ self.value = value self.style = style if tag is not None: - self.yaml_set_tag(tag) + if isinstance(tag, str): + tag = Tag(suffix=tag) + self.yaml_set_ctag(tag) def __str__(self) -> Any: return self.value diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/ruamel.yaml-0.17.26/composer.py new/ruamel.yaml-0.17.31/composer.py --- old/ruamel.yaml-0.17.26/composer.py 2023-05-02 06:22:32.000000000 +0200 +++ new/ruamel.yaml-0.17.31/composer.py 2023-05-28 08:45:22.000000000 +0200 @@ -32,6 +32,7 @@ if self.loader is not None and getattr(self.loader, '_composer', None) is None: self.loader._composer = self self.anchors: Dict[Any, Any] = {} + self.warn_double_anchors = True @property def parser(self) -> Any: @@ -111,7 +112,7 @@ event = self.parser.peek_event() anchor = event.anchor if anchor is not None: # have an anchor - if anchor in self.anchors: + if self.warn_double_anchors and anchor in self.anchors: ws = ( f'\nfound duplicate anchor {anchor!r}\n' f'first occurrence {self.anchors[anchor].start_mark}\n' @@ -130,9 +131,11 @@ def compose_scalar_node(self, anchor: Any) -> Any: event = self.parser.get_event() - tag = event.tag - if tag is None or tag == '!': + tag = event.ctag + if tag is None or str(tag) == '!': tag = self.resolver.resolve(ScalarNode, event.value, event.implicit) + assert not isinstance(tag, str) + # e.g tag.yaml.org,2002:str node = ScalarNode( tag, event.value, @@ -148,9 +151,10 @@ def compose_sequence_node(self, anchor: Any) -> Any: start_event = self.parser.get_event() - tag = start_event.tag - if tag is None or tag == '!': + tag = start_event.ctag + if tag is None or str(tag) == '!': tag = self.resolver.resolve(SequenceNode, None, start_event.implicit) + assert not isinstance(tag, str) node = SequenceNode( tag, [], @@ -180,9 +184,10 @@ def compose_mapping_node(self, anchor: Any) -> Any: start_event = self.parser.get_event() - tag = start_event.tag - if tag is None or tag == '!': + tag = start_event.ctag + if tag is None or str(tag) == '!': tag = self.resolver.resolve(MappingNode, None, start_event.implicit) + assert not isinstance(tag, str) node = MappingNode( tag, [], diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/ruamel.yaml-0.17.26/constructor.py new/ruamel.yaml-0.17.31/constructor.py --- old/ruamel.yaml-0.17.26/constructor.py 2023-05-02 06:22:32.000000000 +0200 +++ new/ruamel.yaml-0.17.31/constructor.py 2023-05-30 07:32:48.000000000 +0200 @@ -986,6 +986,17 @@ return SingleQuotedScalarString(node.value, anchor=node.anchor) if node.style == '"': return DoubleQuotedScalarString(node.value, anchor=node.anchor) + # if node.ctag: + # data2 = TaggedScalar() + # data2.value = node.value + # data2.style = node.style + # data2.yaml_set_ctag(node.ctag) + # if node.anchor: + # from ruamel.yaml.serializer import templated_id + + # if not templated_id(node.anchor): + # data2.yaml_set_anchor(node.anchor, always_dump=True) + # return data2 if node.anchor: return PlainScalarString(node.value, anchor=node.anchor) return node.value @@ -1162,7 +1173,10 @@ ) def construct_yaml_str(self, node: Any) -> Any: - value = self.construct_scalar(node) + if node.ctag.handle: + value = self.construct_unknown(node) + else: + value = self.construct_scalar(node) if isinstance(value, ScalarString): return value return value @@ -1218,7 +1232,7 @@ if value_node in self.constructed_objects: value = self.constructed_objects[value_node] else: - value = self.construct_object(value_node, deep=False) + value = self.construct_object(value_node, deep=True) return value # merge = [] @@ -1569,7 +1583,7 @@ data.fa.set_flow_style() elif node.flow_style is False: data.fa.set_block_style() - data.yaml_set_tag(node.tag) + data.yaml_set_ctag(node.ctag) yield data if node.anchor: from ruamel.yaml.serializer import templated_id @@ -1582,7 +1596,7 @@ data2 = TaggedScalar() data2.value = self.construct_scalar(node) data2.style = node.style - data2.yaml_set_tag(node.tag) + data2.yaml_set_ctag(node.ctag) yield data2 if node.anchor: from ruamel.yaml.serializer import templated_id @@ -1597,7 +1611,7 @@ data3.fa.set_flow_style() elif node.flow_style is False: data3.fa.set_block_style() - data3.yaml_set_tag(node.tag) + data3.yaml_set_ctag(node.ctag) yield data3 if node.anchor: from ruamel.yaml.serializer import templated_id diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/ruamel.yaml-0.17.26/emitter.py new/ruamel.yaml-0.17.31/emitter.py --- old/ruamel.yaml-0.17.26/emitter.py 2023-05-09 21:49:20.000000000 +0200 +++ new/ruamel.yaml-0.17.31/emitter.py 2023-05-30 07:34:24.000000000 +0200 @@ -748,7 +748,7 @@ and self.event.tag is not None ): if self.prepared_tag is None: - self.prepared_tag = self.prepare_tag(self.event.tag) + self.prepared_tag = self.prepare_tag(self.event.ctag) length += len(self.prepared_tag) if isinstance(self.event, ScalarEvent): if self.analysis is None: @@ -813,7 +813,7 @@ if tag is None: raise EmitterError('tag is not specified') if self.prepared_tag is None: - self.prepared_tag = self.prepare_tag(tag) + self.prepared_tag = self.prepare_tag(self.event.ctag) if self.prepared_tag: self.write_indicator(self.prepared_tag, True) if ( @@ -825,6 +825,9 @@ self.prepared_tag = None def choose_scalar_style(self) -> Any: + # issue 449 needs this otherwise emits single quoted empty string + if self.event.value == '' and self.event.ctag.handle == '!!': + return None if self.analysis is None: self.analysis = self.analyze_scalar(self.event.value) if self.event.style == '"' or self.canonical: @@ -956,6 +959,7 @@ def prepare_tag(self, tag: Any) -> Any: if not tag: raise EmitterError('tag must not be empty') + tag = str(tag) if tag == '!' or tag == '!!': return tag handle = None @@ -1723,3 +1727,26 @@ comment = event.comment[0] self.write_comment(comment) return True + + +class RoundTripEmitter(Emitter): + def prepare_tag(self, ctag: Any) -> Any: + if not ctag: + raise EmitterError('tag must not be empty') + tag = str(ctag) + # print('handling', repr(tag)) + if tag == '!' or tag == '!!': + return tag + handle = ctag.handle + suffix = ctag.suffix + prefixes = sorted(self.tag_prefixes.keys()) + # print('handling', repr(tag), repr(suffix), repr(handle)) + if handle is None: + for prefix in prefixes: + if tag.startswith(prefix) and (prefix == '!' or len(prefix) < len(tag)): + handle = self.tag_prefixes[prefix] + suffix = suffix[len(prefix) :] + if handle: + return f'{handle!s}{suffix!s}' + else: + return f'!<{suffix!s}>' diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/ruamel.yaml-0.17.26/events.py new/ruamel.yaml-0.17.31/events.py --- old/ruamel.yaml-0.17.26/events.py 2023-05-03 09:46:17.000000000 +0200 +++ new/ruamel.yaml-0.17.31/events.py 2023-05-31 07:51:50.000000000 +0200 @@ -3,6 +3,7 @@ # Abstract classes. from typing import Any, Dict, Optional, List # NOQA +from ruamel.yaml.tag import Tag SHOW_LINES = False @@ -13,6 +14,7 @@ class Event: __slots__ = 'start_mark', 'end_mark', 'comment' + crepr = 'Unspecified Event' def __init__( self, start_mark: Any = None, end_mark: Any = None, comment: Any = CommentCheck @@ -55,6 +57,9 @@ arguments += f', comment={self.comment!r}' return f'{self.__class__.__name__!s}({arguments!s})' + def compact_repr(self) -> str: + return f'{self.crepr}' + class NodeEvent(Event): __slots__ = ('anchor',) @@ -67,7 +72,7 @@ class CollectionStartEvent(NodeEvent): - __slots__ = 'tag', 'implicit', 'flow_style', 'nr_items' + __slots__ = 'ctag', 'implicit', 'flow_style', 'nr_items' def __init__( self, @@ -81,11 +86,15 @@ nr_items: Optional[int] = None, ) -> None: NodeEvent.__init__(self, anchor, start_mark, end_mark, comment) - self.tag = tag + self.ctag = tag self.implicit = implicit self.flow_style = flow_style self.nr_items = nr_items + @property + def tag(self) -> Optional[str]: + return None if self.ctag is None else str(self.ctag) + class CollectionEndEvent(Event): __slots__ = () @@ -96,6 +105,7 @@ class StreamStartEvent(Event): __slots__ = ('encoding',) + crepr = '+STR' def __init__( self, @@ -110,10 +120,12 @@ class StreamEndEvent(Event): __slots__ = () + crepr = '-STR' class DocumentStartEvent(Event): __slots__ = 'explicit', 'version', 'tags' + crepr = '+DOC' def __init__( self, @@ -129,9 +141,14 @@ self.version = version self.tags = tags + def compact_repr(self) -> str: + start = ' ---' if self.explicit else '' + return f'{self.crepr}{start}' + class DocumentEndEvent(Event): __slots__ = ('explicit',) + crepr = '-DOC' def __init__( self, @@ -143,9 +160,14 @@ Event.__init__(self, start_mark, end_mark, comment) self.explicit = explicit + def compact_repr(self) -> str: + end = ' ...' if self.explicit else '' + return f'{self.crepr}{end}' + class AliasEvent(NodeEvent): __slots__ = 'style' + crepr = '=ALI' def __init__( self, @@ -158,9 +180,13 @@ NodeEvent.__init__(self, anchor, start_mark, end_mark, comment) self.style = style + def compact_repr(self) -> str: + return f'{self.crepr} *{self.anchor}' + class ScalarEvent(NodeEvent): - __slots__ = 'tag', 'implicit', 'value', 'style' + __slots__ = 'ctag', 'implicit', 'value', 'style' + crepr = '=VAL' def __init__( self, @@ -174,23 +200,65 @@ comment: Any = None, ) -> None: NodeEvent.__init__(self, anchor, start_mark, end_mark, comment) - self.tag = tag + self.ctag = tag self.implicit = implicit self.value = value self.style = style + @property + def tag(self) -> Optional[str]: + return None if self.ctag is None else str(self.ctag) + + @tag.setter + def tag(self, val: Any) -> None: + if isinstance(val, str): + val = Tag(suffix=val) + self.ctag = val + + def compact_repr(self) -> str: + style = ':' if self.style is None else self.style + anchor = f'&{self.anchor} ' if self.anchor else '' + tag = f'<{self.tag!s}> ' if self.tag else '' + value = self.value + for ch, rep in [ + ('\\', '\\\\'), + ('\t', '\\t'), + ('\n', '\\n'), + ('\a', ''), # remove from folded + ('\r', '\\r'), + ('\b', '\\b'), + ]: + value = value.replace(ch, rep) + return f'{self.crepr} {anchor}{tag}{style}{value}' + class SequenceStartEvent(CollectionStartEvent): __slots__ = () + crepr = '+SEQ' + + def compact_repr(self) -> str: + flow = ' []' if self.flow_style else '' + anchor = f' &{self.anchor}' if self.anchor else '' + tag = f' <{self.tag!s}>' if self.tag else '' + return f'{self.crepr}{flow}{anchor}{tag}' class SequenceEndEvent(CollectionEndEvent): __slots__ = () + crepr = '-SEQ' class MappingStartEvent(CollectionStartEvent): __slots__ = () + crepr = '+MAP' + + def compact_repr(self) -> str: + flow = ' {}' if self.flow_style else '' + anchor = f' &{self.anchor}' if self.anchor else '' + tag = f' <{self.tag!s}>' if self.tag else '' + return f'{self.crepr}{flow}{anchor}{tag}' class MappingEndEvent(CollectionEndEvent): __slots__ = () + crepr = '-MAP' diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/ruamel.yaml-0.17.26/main.py new/ruamel.yaml-0.17.31/main.py --- old/ruamel.yaml-0.17.26/main.py 2023-05-05 20:07:12.000000000 +0200 +++ new/ruamel.yaml-0.17.31/main.py 2023-05-28 16:39:30.000000000 +0200 @@ -118,7 +118,7 @@ elif 'rtsc' in self.typ: self.default_flow_style = False # no optimized rt-dumper yet - self.Emitter = ruamel.yaml.emitter.Emitter + self.Emitter = ruamel.yaml.emitter.RoundTripEmitter self.Serializer = ruamel.yaml.serializer.Serializer self.Representer = ruamel.yaml.representer.RoundTripRepresenter self.Scanner = ruamel.yaml.scanner.RoundTripScannerSC @@ -133,7 +133,7 @@ if setup_rt: self.default_flow_style = False # no optimized rt-dumper yet - self.Emitter = ruamel.yaml.emitter.Emitter + self.Emitter = ruamel.yaml.emitter.RoundTripEmitter self.Serializer = ruamel.yaml.serializer.Serializer self.Representer = ruamel.yaml.representer.RoundTripRepresenter self.Scanner = ruamel.yaml.scanner.RoundTripScanner diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/ruamel.yaml-0.17.26/nodes.py new/ruamel.yaml-0.17.31/nodes.py --- old/ruamel.yaml-0.17.26/nodes.py 2023-05-02 06:22:32.000000000 +0200 +++ new/ruamel.yaml-0.17.31/nodes.py 2023-05-31 07:51:15.000000000 +0200 @@ -2,11 +2,12 @@ import sys -from typing import Dict, Any, Text # NOQA +from typing import Dict, Any, Text, Optional # NOQA +from ruamel.yaml.tag import Tag class Node: - __slots__ = 'tag', 'value', 'start_mark', 'end_mark', 'comment', 'anchor' + __slots__ = 'ctag', 'value', 'start_mark', 'end_mark', 'comment', 'anchor' def __init__( self, @@ -17,13 +18,24 @@ comment: Any = None, anchor: Any = None, ) -> None: - self.tag = tag + # you can still get a string from the serializer + self.ctag = tag if isinstance(tag, Tag) else Tag(suffix=tag) self.value = value self.start_mark = start_mark self.end_mark = end_mark self.comment = comment self.anchor = anchor + @property + def tag(self) -> Optional[str]: + return None if self.ctag is None else str(self.ctag) + + @tag.setter + def tag(self, val: Any) -> None: + if isinstance(val, str): + val = Tag(suffix=val) + self.ctag = val + def __repr__(self) -> Any: value = self.value # if isinstance(value, list): diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/ruamel.yaml-0.17.26/parser.py new/ruamel.yaml-0.17.31/parser.py --- old/ruamel.yaml-0.17.26/parser.py 2023-05-02 06:22:32.000000000 +0200 +++ new/ruamel.yaml-0.17.31/parser.py 2023-05-30 07:39:57.000000000 +0200 @@ -81,6 +81,7 @@ from ruamel.yaml.scanner import BlankLineComment from ruamel.yaml.comments import C_PRE, C_POST, C_SPLIT_ON_FIRST_BLANK from ruamel.yaml.compat import nprint, nprintf # NOQA +from ruamel.yaml.tag import Tag from typing import Any, Dict, Optional, List, Optional # NOQA @@ -182,6 +183,7 @@ def parse_implicit_document_start(self) -> Any: # Parse an implicit document. if not self.scanner.check_token(DirectiveToken, DocumentStartToken, StreamEndToken): + # don't need copy, as an implicit tag doesn't add tag_handles self.tag_handles = self.DEFAULT_TAGS token = self.scanner.peek_token() start_mark = end_mark = token.start_mark @@ -243,6 +245,18 @@ explicit = False if self.scanner.check_token(DocumentEndToken): token = self.scanner.get_token() + # if token.end_mark.line != self.peek_event().start_mark.line: + pt = self.scanner.peek_token() + if not isinstance(pt, StreamEndToken) and ( + token.end_mark.line == pt.start_mark.line + ): + raise ParserError( + None, + None, + 'found non-comment content after document end marker, ' + f'{self.scanner.peek_token().id,!r}', + self.scanner.peek_token().start_mark, + ) end_mark = token.end_mark explicit = True event = DocumentEndEvent(start_mark, end_mark, explicit=explicit) @@ -251,7 +265,11 @@ if self.resolver.processing_version == (1, 1): self.state = self.parse_document_start else: - self.state = self.parse_implicit_document_start + if explicit: + # found a document end marker, can be followed by implicit document + self.state = self.parse_implicit_document_start + else: + self.state = self.parse_document_start return event @@ -331,8 +349,13 @@ def parse_block_node_or_indentless_sequence(self) -> Any: return self.parse_node(block=True, indentless_sequence=True) - def transform_tag(self, handle: Any, suffix: Any) -> Any: - return self.tag_handles[handle] + suffix + # def transform_tag(self, handle: Any, suffix: Any) -> Any: + # return self.tag_handles[handle] + suffix + + def select_tag_transform(self, tag: Tag) -> None: + if tag is None: + return + tag.select_transform(False) def parse_node(self, block: bool = False, indentless_sequence: bool = False) -> Any: if self.scanner.check_token(AliasToken): @@ -354,39 +377,34 @@ token = self.scanner.get_token() tag_mark = token.start_mark end_mark = token.end_mark - tag = token.value + # tag = token.value + tag = Tag( + handle=token.value[0], suffix=token.value[1], handles=self.tag_handles, + ) elif self.scanner.check_token(TagToken): token = self.scanner.get_token() start_mark = tag_mark = token.start_mark end_mark = token.end_mark - tag = token.value + # tag = token.value + tag = Tag(handle=token.value[0], suffix=token.value[1], handles=self.tag_handles) if self.scanner.check_token(AnchorToken): token = self.scanner.get_token() start_mark = tag_mark = token.start_mark end_mark = token.end_mark anchor = token.value if tag is not None: - handle, suffix = tag - if handle is not None: - if handle not in self.tag_handles: - raise ParserError( - 'while parsing a node', - start_mark, - f'found undefined tag handle {handle!r}', - tag_mark, - ) - tag = self.transform_tag(handle, suffix) - else: - tag = suffix - # if tag == '!': - # raise ParserError("while parsing a node", start_mark, - # "found non-specific tag '!'", tag_mark, - # "Please check 'http://pyyaml.org/wiki/YAMLNonSpecificTag' - # and share your opinion.") + self.select_tag_transform(tag) + if tag.check_handle(): + raise ParserError( + 'while parsing a node', + start_mark, + f'found undefined tag handle {tag.handle!r}', + tag_mark, + ) if start_mark is None: start_mark = end_mark = self.scanner.peek_token().start_mark event = None - implicit = tag is None or tag == '!' + implicit = tag is None or str(tag) == '!' if indentless_sequence and self.scanner.check_token(BlockEntryToken): comment = None pt = self.scanner.peek_token() @@ -399,7 +417,7 @@ comment = pt.comment end_mark = self.scanner.peek_token().end_mark event = SequenceStartEvent( - anchor, tag, implicit, start_mark, end_mark, flow_style=False, comment=comment + anchor, tag, implicit, start_mark, end_mark, flow_style=False, comment=comment, ) self.state = self.parse_indentless_sequence_entry return event @@ -408,17 +426,17 @@ token = self.scanner.get_token() # self.scanner.peek_token_same_line_comment(token) end_mark = token.end_mark - if (token.plain and tag is None) or tag == '!': - implicit = (True, False) + if (token.plain and tag is None) or str(tag) == '!': + dimplicit = (True, False) elif tag is None: - implicit = (False, True) + dimplicit = (False, True) else: - implicit = (False, False) + dimplicit = (False, False) # nprint('se', token.value, token.comment) event = ScalarEvent( anchor, tag, - implicit, + dimplicit, token.value, start_mark, end_mark, @@ -775,24 +793,10 @@ class RoundTripParser(Parser): """roundtrip is a safe loader, that wants to see the unmangled tag""" - def transform_tag(self, handle: Any, suffix: Any) -> Any: - # return self.tag_handles[handle]+suffix - if handle == '!!' and suffix in ( - 'null', - 'bool', - 'int', - 'float', - 'binary', - 'timestamp', - 'omap', - 'pairs', - 'set', - 'str', - 'seq', - 'map', - ): - return Parser.transform_tag(self, handle, suffix) - return handle + suffix + def select_tag_transform(self, tag: Tag) -> None: + if tag is None: + return + tag.select_transform(True) def move_token_comment( self, token: Any, nt: Optional[Any] = None, empty: Optional[bool] = False diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/ruamel.yaml-0.17.26/representer.py new/ruamel.yaml-0.17.31/representer.py --- old/ruamel.yaml-0.17.26/representer.py 2023-05-02 06:22:32.000000000 +0200 +++ new/ruamel.yaml-0.17.31/representer.py 2023-05-29 07:53:10.000000000 +0200 @@ -148,6 +148,8 @@ comment = getattr(value, 'comment', None) if comment: comment = [None, [comment]] + if isinstance(tag, str): + tag = Tag(suffix=tag) node = ScalarNode(tag, value, style=style, comment=comment, anchor=anchor) if self.alias_key is not None: self.represented_objects[self.alias_key] = node @@ -157,6 +159,8 @@ self, tag: Any, sequence: Any, flow_style: Any = None ) -> SequenceNode: value: List[Any] = [] + if isinstance(tag, str): + tag = Tag(suffix=tag) node = SequenceNode(tag, value, flow_style=flow_style) if self.alias_key is not None: self.represented_objects[self.alias_key] = node @@ -175,6 +179,8 @@ def represent_omap(self, tag: Any, omap: Any, flow_style: Any = None) -> SequenceNode: value: List[Any] = [] + if isinstance(tag, str): + tag = Tag(suffix=tag) node = SequenceNode(tag, value, flow_style=flow_style) if self.alias_key is not None: self.represented_objects[self.alias_key] = node @@ -195,6 +201,8 @@ def represent_mapping(self, tag: Any, mapping: Any, flow_style: Any = None) -> MappingNode: value: List[Any] = [] + if isinstance(tag, str): + tag = Tag(suffix=tag) node = MappingNode(tag, value, flow_style=flow_style) if self.alias_key is not None: self.represented_objects[self.alias_key] = node @@ -709,6 +717,8 @@ anchor = sequence.yaml_anchor() except AttributeError: anchor = None + if isinstance(tag, str): + tag = Tag(suffix=tag) node = SequenceNode(tag, value, flow_style=flow_style, anchor=anchor) if self.alias_key is not None: self.represented_objects[self.alias_key] = node @@ -784,6 +794,8 @@ anchor = mapping.yaml_anchor() except AttributeError: anchor = None + if isinstance(tag, str): + tag = Tag(suffix=tag) node = MappingNode(tag, value, flow_style=flow_style, anchor=anchor) if self.alias_key is not None: self.represented_objects[self.alias_key] = node @@ -858,7 +870,9 @@ else: arg = self.represent_data(merge_list) arg.flow_style = True - value.insert(merge_pos, (ScalarNode('tag:yaml.org,2002:merge', '<<'), arg)) + value.insert( + merge_pos, (ScalarNode(Tag(suffix='tag:yaml.org,2002:merge'), '<<'), arg) + ) return node def represent_omap(self, tag: Any, omap: Any, flow_style: Any = None) -> SequenceNode: @@ -871,6 +885,8 @@ anchor = omap.yaml_anchor() except AttributeError: anchor = None + if isinstance(tag, str): + tag = Tag(suffix=tag) node = SequenceNode(tag, value, flow_style=flow_style, anchor=anchor) if self.alias_key is not None: self.represented_objects[self.alias_key] = node @@ -926,7 +942,7 @@ def represent_set(self, setting: Any) -> MappingNode: flow_style = False - tag = 'tag:yaml.org,2002:set' + tag = Tag(suffix='tag:yaml.org,2002:set') # return self.represent_mapping(tag, value) value: List[Any] = [] flow_style = setting.fa.flow_style(flow_style) @@ -979,30 +995,32 @@ def represent_dict(self, data: Any) -> MappingNode: """write out tag if saved on loading""" try: - t = data.tag.value + _ = data.tag except AttributeError: - t = None - if t: - if t.startswith('!!'): - tag = 'tag:yaml.org,2002:' + t[2:] - else: - tag = t + tag = Tag(suffix='tag:yaml.org,2002:map') else: - tag = 'tag:yaml.org,2002:map' + if data.tag.trval: + if data.tag.startswith('!!'): + tag = Tag(suffix='tag:yaml.org,2002:' + data.tag.trval[2:]) + else: + tag = data.tag + else: + tag = Tag(suffix='tag:yaml.org,2002:map') return self.represent_mapping(tag, data) def represent_list(self, data: Any) -> SequenceNode: try: - t = data.tag.value + _ = data.tag except AttributeError: - t = None - if t: - if t.startswith('!!'): - tag = 'tag:yaml.org,2002:' + t[2:] - else: - tag = t + tag = Tag(suffix='tag:yaml.org,2002:seq') else: - tag = 'tag:yaml.org,2002:seq' + if data.tag.trval: + if data.tag.startswith('!!'): + tag = Tag(suffix='tag:yaml.org,2002:' + data.tag.trval[2:]) + else: + tag = data.tag + else: + tag = Tag(suffix='tag:yaml.org,2002:seq') return self.represent_sequence(tag, data) def represent_datetime(self, data: Any) -> ScalarNode: @@ -1019,7 +1037,10 @@ def represent_tagged_scalar(self, data: Any) -> ScalarNode: try: - tag = data.tag.value + if data.tag.handle == '!!': + tag = f'{data.tag.handle} {data.tag.suffix}' + else: + tag = data.tag except AttributeError: tag = None try: diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/ruamel.yaml-0.17.26/resolver.py new/ruamel.yaml-0.17.31/resolver.py --- old/ruamel.yaml-0.17.26/resolver.py 2023-05-02 06:22:32.000000000 +0200 +++ new/ruamel.yaml-0.17.31/resolver.py 2023-05-30 07:42:47.000000000 +0200 @@ -5,6 +5,7 @@ from typing import Any, Dict, List, Union, Text, Optional # NOQA from ruamel.yaml.compat import VersionType # NOQA +from ruamel.yaml.tag import Tag from ruamel.yaml.compat import _DEFAULT_YAML_VERSION # NOQA from ruamel.yaml.error import * # NOQA from ruamel.yaml.nodes import MappingNode, ScalarNode, SequenceNode # NOQA @@ -102,9 +103,9 @@ class BaseResolver: - DEFAULT_SCALAR_TAG = 'tag:yaml.org,2002:str' - DEFAULT_SEQUENCE_TAG = 'tag:yaml.org,2002:seq' - DEFAULT_MAPPING_TAG = 'tag:yaml.org,2002:map' + DEFAULT_SCALAR_TAG = Tag(suffix='tag:yaml.org,2002:str') + DEFAULT_SEQUENCE_TAG = Tag(suffix='tag:yaml.org,2002:seq') + DEFAULT_MAPPING_TAG = Tag(suffix='tag:yaml.org,2002:map') yaml_implicit_resolvers: Dict[Any, Any] = {} yaml_path_resolvers: Dict[Any, Any] = {} @@ -268,14 +269,14 @@ resolvers += self.yaml_implicit_resolvers.get(None, []) for tag, regexp in resolvers: if regexp.match(value): - return tag + return Tag(suffix=tag) implicit = implicit[1] if bool(self.yaml_path_resolvers): exact_paths = self.resolver_exact_paths[-1] if kind in exact_paths: - return exact_paths[kind] + return Tag(suffix=exact_paths[kind]) if None in exact_paths: - return exact_paths[None] + return Tag(suffix=exact_paths[None]) if kind is ScalarNode: return self.DEFAULT_SCALAR_TAG elif kind is SequenceNode: @@ -354,14 +355,14 @@ resolvers += self.versioned_resolver.get(None, []) for tag, regexp in resolvers: if regexp.match(value): - return tag + return Tag(suffix=tag) implicit = implicit[1] if bool(self.yaml_path_resolvers): exact_paths = self.resolver_exact_paths[-1] if kind in exact_paths: - return exact_paths[kind] + return Tag(suffix=exact_paths[kind]) if None in exact_paths: - return exact_paths[None] + return Tag(suffix=exact_paths[None]) if kind is ScalarNode: return self.DEFAULT_SCALAR_TAG elif kind is SequenceNode: diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/ruamel.yaml-0.17.26/ruamel.yaml.egg-info/PKG-INFO new/ruamel.yaml-0.17.31/ruamel.yaml.egg-info/PKG-INFO --- old/ruamel.yaml-0.17.26/ruamel.yaml.egg-info/PKG-INFO 2023-05-09 22:00:46.000000000 +0200 +++ new/ruamel.yaml-0.17.31/ruamel.yaml.egg-info/PKG-INFO 2023-05-31 07:58:00.000000000 +0200 @@ -1,6 +1,6 @@ Metadata-Version: 2.1 Name: ruamel.yaml -Version: 0.17.26 +Version: 0.17.31 Summary: ruamel.yaml is a YAML parser/emitter that supports roundtrip preservation of comments, seq/map flow style, and map key order Home-page: https://sourceforge.net/p/ruamel-yaml/code/ci/default/tree Author: Anthon van der Neut @@ -34,8 +34,8 @@ ``ruamel.yaml`` is a YAML 1.2 loader/dumper package for Python. -:version: 0.17.26 -:updated: 2023-05-09 +:version: 0.17.31 +:updated: 2023-05-31 :documentation: http://yaml.readthedocs.io :repository: https://sourceforge.net/projects/ruamel-yaml/ :pypi: https://pypi.org/project/ruamel.yaml/ @@ -91,8 +91,36 @@ .. should insert NEXT: at the beginning of line for next key (with empty line) +0.17.31 (2023-05-31): + - added tag.setter on `ScalarEvent` and on `Node`, that takes either + a `Tag` instance, or a str + (reported by `Sorin Sbarnea <https://sourceforge.net/u/ssbarnea/profile/>`__) + +0.17.30 (2023-05-30): + - fix issue 467, caused by Tag instances not being hashable (reported by + `Douglas Raillard + <https://bitbucket.org/%7Bcf052d92-a278-4339-9aa8-de41923bb556%7D/>`__) + +0.17.29 (2023-05-30): + - changed the internals of the tag property from a string to a class which allows + for preservation of the original handle and suffix. This should + result in better results using documents with %TAG directives, as well + as preserving URI escapes in tag suffixes. + +0.17.28 (2023-05-26): + - fix for issue 464: documents ending with document end marker without final newline + fail to load (reported by `Mariusz Rusiniak <https://sourceforge.net/u/r2dan/profile/>`__) + +0.17.27 (2023-05-25): + - fix issue with inline mappings as value for merge keys + (reported by Sirish on `StackOverflow <https://stackoverflow.com/q/76331049/1307905>`__) + - fix for 468, error inserting after accessing merge attribute on ``CommentedMap`` + (reported by `Bastien gerard <https://sourceforge.net/u/bagerard/>`__) + - fix for issue 461 pop + insert on same `CommentedMap` key throwing error + (reported by `John Thorvald Wodder II <https://sourceforge.net/u/jwodder/profile/>`__) + 0.17.26 (2023-05-09): - - Fix for error on edge cage for issue 459 + - fix for error on edge cage for issue 459 0.17.25 (2023-05-09): - fix for regression while dumping wrapped strings with too many backslashes removed @@ -188,7 +216,7 @@ attrs with `@attr.s()` (both reported by `ssph <https://sourceforge.net/u/sph/>`__) 0.17.11 (2021-08-19): - - fix error baseclass for ``DuplicateKeyErorr`` (reported by `Åukasz Rogalski + - fix error baseclass for ``DuplicateKeyError`` (reported by `Åukasz Rogalski <https://sourceforge.net/u/lrogalski/>`__) - fix typo in reader error message, causing `KeyError` during reader error (reported by `MTU <https://sourceforge.net/u/mtu/>`__) diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/ruamel.yaml-0.17.26/ruamel.yaml.egg-info/SOURCES.txt new/ruamel.yaml-0.17.31/ruamel.yaml.egg-info/SOURCES.txt --- old/ruamel.yaml-0.17.26/ruamel.yaml.egg-info/SOURCES.txt 2023-05-09 22:00:46.000000000 +0200 +++ new/ruamel.yaml-0.17.31/ruamel.yaml.egg-info/SOURCES.txt 2023-05-31 07:58:00.000000000 +0200 @@ -31,6 +31,7 @@ ./scalarstring.py ./scanner.py ./serializer.py +./tag.py ./timestamp.py ./tokens.py ./util.py diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/ruamel.yaml-0.17.26/scanner.py new/ruamel.yaml-0.17.31/scanner.py --- old/ruamel.yaml-0.17.26/scanner.py 2023-05-06 12:19:26.000000000 +0200 +++ new/ruamel.yaml-0.17.31/scanner.py 2023-05-29 13:27:38.000000000 +0200 @@ -1291,16 +1291,25 @@ srp = self.reader.peek srf = self.reader.forward chunks = [] + first_indent = -1 max_indent = 0 end_mark = self.reader.get_mark() while srp() in ' \r\n\x85\u2028\u2029': if srp() != ' ': + if first_indent < 0: + first_indent = self.reader.column chunks.append(self.scan_line_break()) end_mark = self.reader.get_mark() else: srf() if self.reader.column > max_indent: max_indent = self.reader.column + if first_indent > 0 and max_indent > first_indent: + start_mark = self.reader.get_mark() + raise ScannerError( + 'more indented follow up line than first in a block scalar', + start_mark, + ) return chunks, max_indent, end_mark def scan_block_scalar_breaks(self, indent: int) -> Any: @@ -1493,7 +1502,9 @@ break while True: ch = srp(length) - if ch == ':' and srp(length + 1) not in _THE_END_SPACE_TAB: + if ch == ':' and srp(length + 1) == ',': + break + elif ch == ':' and srp(length + 1) not in _THE_END_SPACE_TAB: pass elif ch == '?' and self.scanner_processing_version != (1, 1): pass @@ -1918,6 +1929,37 @@ def scan_block_scalar(self, style: Any, rt: Optional[bool] = True) -> Any: return Scanner.scan_block_scalar(self, style, rt=rt) + def scan_uri_escapes(self, name: Any, start_mark: Any) -> Any: + """ + The roundtripscanner doesn't do URI escaping + """ + # See the specification for details. + srp = self.reader.peek + srf = self.reader.forward + code_bytes: List[Any] = [] + chunk = '' + mark = self.reader.get_mark() + while srp() == '%': + chunk += '%' + srf() + for k in range(2): + if srp(k) not in '0123456789ABCDEFabcdef': + raise ScannerError( + f'while scanning an {name!s}', + start_mark, + f'expected URI escape sequence of 2 hexdecimal numbers, ' + f'but found {srp(k)!r}', + self.reader.get_mark(), + ) + code_bytes.append(int(self.reader.prefix(2), 16)) + chunk += self.reader.prefix(2) + srf(2) + try: + _ = bytes(code_bytes).decode('utf-8') + except UnicodeDecodeError as exc: + raise ScannerError(f'while scanning an {name!s}', start_mark, str(exc), mark) + return chunk + # commenthandling 2021, differentiatiation not needed diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/ruamel.yaml-0.17.26/serializer.py new/ruamel.yaml-0.17.31/serializer.py --- old/ruamel.yaml-0.17.26/serializer.py 2023-05-02 06:22:32.000000000 +0200 +++ new/ruamel.yaml-0.17.31/serializer.py 2023-05-29 07:48:22.000000000 +0200 @@ -158,14 +158,14 @@ detected_tag = self.resolver.resolve(ScalarNode, node.value, (True, False)) default_tag = self.resolver.resolve(ScalarNode, node.value, (False, True)) implicit = ( - (node.tag == detected_tag), - (node.tag == default_tag), - node.tag.startswith('tag:yaml.org,2002:'), + (node.ctag == detected_tag), + (node.ctag == default_tag), + node.tag.startswith('tag:yaml.org,2002:'), # type: ignore ) self.emitter.emit( ScalarEvent( alias, - node.tag, + node.ctag, implicit, node.value, style=node.style, @@ -173,7 +173,7 @@ ) ) elif isinstance(node, SequenceNode): - implicit = node.tag == self.resolver.resolve(SequenceNode, node.value, True) + implicit = node.ctag == self.resolver.resolve(SequenceNode, node.value, True) comment = node.comment end_comment = None seq_comment = None @@ -188,7 +188,7 @@ self.emitter.emit( SequenceStartEvent( alias, - node.tag, + node.ctag, implicit, flow_style=node.flow_style, comment=node.comment, @@ -200,7 +200,7 @@ index += 1 self.emitter.emit(SequenceEndEvent(comment=[seq_comment, end_comment])) elif isinstance(node, MappingNode): - implicit = node.tag == self.resolver.resolve(MappingNode, node.value, True) + implicit = node.ctag == self.resolver.resolve(MappingNode, node.value, True) comment = node.comment end_comment = None map_comment = None @@ -213,7 +213,7 @@ self.emitter.emit( MappingStartEvent( alias, - node.tag, + node.ctag, implicit, flow_style=node.flow_style, comment=node.comment, diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/ruamel.yaml-0.17.26/tag.py new/ruamel.yaml-0.17.31/tag.py --- old/ruamel.yaml-0.17.26/tag.py 1970-01-01 01:00:00.000000000 +0100 +++ new/ruamel.yaml-0.17.31/tag.py 2023-05-30 14:47:15.000000000 +0200 @@ -0,0 +1,122 @@ +# coding: utf-8 + +""" +In round-trip mode the original tag needs to be preserved, but the tag +transformed based on the directives needs to be available as well. + +A Tag that is created during loading has a handle and a suffix. +Not all objects loaded currently have a Tag, that .tag attribute can be None +A Tag that is created for dumping only (on an object loaded without a tag) has a suffix +only. +""" + +from typing import Any, Dict, Optional, List, Union, Optional, Iterator # NOQA + +tag_attrib = '_yaml_tag' + + +class Tag: + """store original tag information for roundtripping""" + + attrib = tag_attrib + + def __init__(self, handle: Any = None, suffix: Any = None, handles: Any = None) -> None: + self.handle = handle + self.suffix = suffix + self.handles = handles + self._transform_type: Optional[bool] = None + + def __repr__(self) -> str: + return f'{self.__class__.__name__}({self.trval!r})' + + def __str__(self) -> str: + return f'{self.trval}' + + def __hash__(self) -> int: + try: + return self._hash_id # type: ignore + except AttributeError: + self._hash_id = res = hash((self.handle, self.suffix)) + return res + + def __eq__(self, other: Any) -> bool: + # other should not be a string, but the serializer sometimes provides these + if isinstance(other, str): + return self.trval == other + return bool(self.trval == other.trval) + + def startswith(self, x: str) -> bool: + if self.trval is not None: + return self.trval.startswith(x) + return False + + @property + def trval(self) -> Optional[str]: + try: + return self._trval + except AttributeError: + pass + if self.handle is None: + self._trval: Optional[str] = self.uri_decoded_suffix + return self._trval + assert self._transform_type is not None + if not self._transform_type: + # the non-round-trip case + self._trval = self.handles[self.handle] + self.uri_decoded_suffix + return self._trval + # round-trip case + if self.handle == '!!' and self.suffix in ( + 'null', + 'bool', + 'int', + 'float', + 'binary', + 'timestamp', + 'omap', + 'pairs', + 'set', + 'str', + 'seq', + 'map', + ): + self._trval = self.handles[self.handle] + self.uri_decoded_suffix + else: + # self._trval = self.handle + self.suffix + self._trval = self.handles[self.handle] + self.uri_decoded_suffix + return self._trval + + @property + def uri_decoded_suffix(self) -> Optional[str]: + try: + return self._uri_decoded_suffix + except AttributeError: + pass + if self.suffix is None: + self._uri_decoded_suffix: Optional[str] = None + return None + res = '' + # don't have to check for scanner errors here + idx = 0 + while idx < len(self.suffix): + ch = self.suffix[idx] + idx += 1 + if ch != '%': + res += ch + else: + res += chr(int(self.suffix[idx : idx + 2], 16)) + idx += 2 + self._uri_decoded_suffix = res + return res + + def select_transform(self, val: bool) -> None: + """ + val: False -> non-round-trip + True -> round-trip + """ + assert self._transform_type is None + self._transform_type = val + + def check_handle(self) -> bool: + if self.handle is None: + return False + return self.handle not in self.handles diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/ruamel.yaml-0.17.26/timestamp.py new/ruamel.yaml-0.17.31/timestamp.py --- old/ruamel.yaml-0.17.26/timestamp.py 2023-05-02 06:22:32.000000000 +0200 +++ new/ruamel.yaml-0.17.31/timestamp.py 2023-05-29 13:14:11.000000000 +0200 @@ -5,6 +5,8 @@ # ToDo: at least on PY3 you could probably attach the tzinfo correctly to the object # a more complete datetime might be used by safe loading as well +# +# add type information (iso8601, spaced) from typing import Any, Dict, Optional, List # NOQA
