summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorDaniel Baumann <daniel.baumann@progress-linux.org>2024-05-04 00:33:55 +0000
committerDaniel Baumann <daniel.baumann@progress-linux.org>2024-05-04 00:33:55 +0000
commitcbbc936ed9811bdb5dd480bc2c5e10c3062532be (patch)
treeec1783c0aaa2ee6eaa6d6362f2bed4392943de8e
parentReleasing progress-linux version 0.18.5-1~exp1~progress7.99u1. (diff)
downloadruamel.yaml-cbbc936ed9811bdb5dd480bc2c5e10c3062532be.tar.xz
ruamel.yaml-cbbc936ed9811bdb5dd480bc2c5e10c3062532be.zip
Merging upstream version 0.18.6.
Signed-off-by: Daniel Baumann <daniel.baumann@progress-linux.org>
-rw-r--r--.hgignore20
-rw-r--r--.hgtags234
-rw-r--r--.readthedocs.yaml20
-rw-r--r--CHANGES7
-rw-r--r--LICENSE2
-rw-r--r--PKG-INFO430
-rw-r--r--README.md13
-rw-r--r--__init__.py9
-rw-r--r--_doc/Makefile216
-rw-r--r--_doc/README.ryd120
-rw-r--r--_doc/_static/license.svg1
-rw-r--r--_doc/_static/pypi.svg1
-rw-r--r--_doc/api.ryd308
-rw-r--r--_doc/basicuse.ryd77
-rw-r--r--_doc/conf.py317
-rw-r--r--_doc/contributing.ryd139
-rw-r--r--_doc/detail.ryd298
-rw-r--r--_doc/dumpcls.ryd315
-rw-r--r--_doc/example.ryd255
-rw-r--r--_doc/install.ryd47
-rw-r--r--_doc/links.rydinc7
-rw-r--r--_doc/overview.ryd47
-rw-r--r--_doc/pyyaml.ryd72
-rw-r--r--_test/data/a-nasty-libyaml-bug.loader-error1
-rw-r--r--_test/data/aliases-cdumper-bug.code1
-rw-r--r--_test/data/aliases.events8
-rw-r--r--_test/data/bool.data18
-rw-r--r--_test/data/bool.detect1
-rw-r--r--_test/data/colon-in-flow-context.loader-error1
-rw-r--r--_test/data/comment_no_eol.data1
-rw-r--r--_test/data/composite_key.code1
-rw-r--r--_test/data/composite_key.data4
-rw-r--r--_test/data/construct-binary-py2.code7
-rw-r--r--_test/data/construct-binary-py2.data12
-rw-r--r--_test/data/construct-binary-py3.code7
-rw-r--r--_test/data/construct-binary-py3.data12
-rw-r--r--_test/data/construct-bool.code7
-rw-r--r--_test/data/construct-bool.data9
-rw-r--r--_test/data/construct-custom.code10
-rw-r--r--_test/data/construct-custom.data26
-rw-r--r--_test/data/construct-float.code8
-rw-r--r--_test/data/construct-float.data6
-rw-r--r--_test/data/construct-int.code8
-rw-r--r--_test/data/construct-int.data6
-rw-r--r--_test/data/construct-map.code6
-rw-r--r--_test/data/construct-map.data6
-rw-r--r--_test/data/construct-merge.code10
-rw-r--r--_test/data/construct-merge.data27
-rw-r--r--_test/data/construct-null.code13
-rw-r--r--_test/data/construct-null.data18
-rw-r--r--_test/data/construct-omap.code8
-rw-r--r--_test/data/construct-omap.data8
-rw-r--r--_test/data/construct-pairs.code9
-rw-r--r--_test/data/construct-pairs.data7
-rw-r--r--_test/data/construct-python-bool.code1
-rw-r--r--_test/data/construct-python-bool.data1
-rw-r--r--_test/data/construct-python-bytes-py3.code1
-rw-r--r--_test/data/construct-python-bytes-py3.data1
-rw-r--r--_test/data/construct-python-complex.code1
-rw-r--r--_test/data/construct-python-complex.data8
-rw-r--r--_test/data/construct-python-float.code1
-rw-r--r--_test/data/construct-python-float.data1
-rw-r--r--_test/data/construct-python-int.code1
-rw-r--r--_test/data/construct-python-int.data1
-rw-r--r--_test/data/construct-python-long-short-py2.code1
-rw-r--r--_test/data/construct-python-long-short-py2.data1
-rw-r--r--_test/data/construct-python-long-short-py3.code1
-rw-r--r--_test/data/construct-python-long-short-py3.data1
-rw-r--r--_test/data/construct-python-name-module.code1
-rw-r--r--_test/data/construct-python-name-module.data5
-rw-r--r--_test/data/construct-python-none.code1
-rw-r--r--_test/data/construct-python-none.data1
-rw-r--r--_test/data/construct-python-object.code23
-rw-r--r--_test/data/construct-python-object.data21
-rw-r--r--_test/data/construct-python-str-ascii.code1
-rw-r--r--_test/data/construct-python-str-ascii.data1
-rw-r--r--_test/data/construct-python-str-utf8-py2.code1
-rw-r--r--_test/data/construct-python-str-utf8-py2.data1
-rw-r--r--_test/data/construct-python-str-utf8-py3.code1
-rw-r--r--_test/data/construct-python-str-utf8-py3.data1
-rw-r--r--_test/data/construct-python-tuple-list-dict.code6
-rw-r--r--_test/data/construct-python-tuple-list-dict.data8
-rw-r--r--_test/data/construct-python-unicode-ascii-py2.code1
-rw-r--r--_test/data/construct-python-unicode-ascii-py2.data1
-rw-r--r--_test/data/construct-python-unicode-ascii-py3.code1
-rw-r--r--_test/data/construct-python-unicode-ascii-py3.data1
-rw-r--r--_test/data/construct-python-unicode-utf8-py2.code1
-rw-r--r--_test/data/construct-python-unicode-utf8-py2.data1
-rw-r--r--_test/data/construct-python-unicode-utf8-py3.code1
-rw-r--r--_test/data/construct-python-unicode-utf8-py3.data1
-rw-r--r--_test/data/construct-seq.code4
-rw-r--r--_test/data/construct-seq.data15
-rw-r--r--_test/data/construct-set.code4
-rw-r--r--_test/data/construct-set.data7
-rw-r--r--_test/data/construct-str-ascii.code1
-rw-r--r--_test/data/construct-str-ascii.data1
-rw-r--r--_test/data/construct-str-utf8-py2.code1
-rw-r--r--_test/data/construct-str-utf8-py2.data1
-rw-r--r--_test/data/construct-str-utf8-py3.code1
-rw-r--r--_test/data/construct-str-utf8-py3.data1
-rw-r--r--_test/data/construct-str.code1
-rw-r--r--_test/data/construct-str.data1
-rw-r--r--_test/data/construct-timestamp.code7
-rw-r--r--_test/data/construct-timestamp.data5
-rw-r--r--_test/data/construct-value.code9
-rw-r--r--_test/data/construct-value.data10
-rw-r--r--_test/data/document-separator-in-quoted-scalar.loader-error11
-rw-r--r--_test/data/documents.events11
-rw-r--r--_test/data/duplicate-anchor-1.loader-warning3
-rw-r--r--_test/data/duplicate-anchor-2.loader-warning1
-rw-r--r--_test/data/duplicate-merge-key.former-loader-error.code1
-rw-r--r--_test/data/duplicate-tag-directive.loader-error3
-rw-r--r--_test/data/duplicate-yaml-directive.loader-error3
-rw-r--r--_test/data/emit-block-scalar-in-simple-key-context-bug.canonical6
-rw-r--r--_test/data/emit-block-scalar-in-simple-key-context-bug.data4
-rw-r--r--_test/data/emitting-unacceptable-unicode-character-bug-py2.code1
-rw-r--r--_test/data/emitting-unacceptable-unicode-character-bug-py2.data1
-rw-r--r--_test/data/emitting-unacceptable-unicode-character-bug-py2.skip-ext0
-rw-r--r--_test/data/emitting-unacceptable-unicode-character-bug-py3.code1
-rw-r--r--_test/data/emitting-unacceptable-unicode-character-bug-py3.data1
-rw-r--r--_test/data/emitting-unacceptable-unicode-character-bug-py3.skip-ext0
-rw-r--r--_test/data/empty-anchor.emitter-error5
-rw-r--r--_test/data/empty-document-bug.canonical1
-rw-r--r--_test/data/empty-document-bug.data0
-rw-r--r--_test/data/empty-document-bug.empty0
-rw-r--r--_test/data/empty-documents.single-loader-error2
-rw-r--r--_test/data/empty-python-module.loader-error1
-rw-r--r--_test/data/empty-python-name.loader-error1
-rw-r--r--_test/data/empty-tag-handle.emitter-error5
-rw-r--r--_test/data/empty-tag-prefix.emitter-error5
-rw-r--r--_test/data/empty-tag.emitter-error5
-rw-r--r--_test/data/expected-document-end.emitter-error6
-rw-r--r--_test/data/expected-document-start.emitter-error4
-rw-r--r--_test/data/expected-mapping.loader-error1
-rw-r--r--_test/data/expected-node-1.emitter-error4
-rw-r--r--_test/data/expected-node-2.emitter-error7
-rw-r--r--_test/data/expected-nothing.emitter-error4
-rw-r--r--_test/data/expected-scalar.loader-error1
-rw-r--r--_test/data/expected-sequence.loader-error1
-rw-r--r--_test/data/expected-stream-start.emitter-error2
-rw-r--r--_test/data/explicit-document.single-loader-error4
-rw-r--r--_test/data/fetch-complex-value-bug.loader-error2
-rw-r--r--_test/data/float-representer-2.3-bug.code7
-rw-r--r--_test/data/float-representer-2.3-bug.data5
-rw-r--r--_test/data/float.data6
-rw-r--r--_test/data/float.detect1
-rw-r--r--_test/data/forbidden-entry.loader-error2
-rw-r--r--_test/data/forbidden-key.loader-error2
-rw-r--r--_test/data/forbidden-value.loader-error1
-rw-r--r--_test/data/implicit-document.single-loader-error3
-rw-r--r--_test/data/int.data7
-rw-r--r--_test/data/int.detect1
-rw-r--r--_test/data/invalid-anchor-1.loader-error1
-rw-r--r--_test/data/invalid-anchor-2.loader-error8
-rw-r--r--_test/data/invalid-anchor.emitter-error5
-rw-r--r--_test/data/invalid-base64-data-2.loader-error2
-rw-r--r--_test/data/invalid-base64-data.loader-error2
-rw-r--r--_test/data/invalid-block-scalar-indicator.loader-error2
-rw-r--r--_test/data/invalid-character.loader-errorbin0 -> 2209 bytes
-rw-r--r--_test/data/invalid-character.stream-errorbin0 -> 4193 bytes
-rw-r--r--_test/data/invalid-directive-line.loader-error2
-rw-r--r--_test/data/invalid-directive-name-1.loader-error2
-rw-r--r--_test/data/invalid-directive-name-2.loader-error2
-rw-r--r--_test/data/invalid-escape-character.loader-error1
-rw-r--r--_test/data/invalid-escape-numbers.loader-error1
-rw-r--r--_test/data/invalid-indentation-indicator-1.loader-error2
-rw-r--r--_test/data/invalid-indentation-indicator-2.loader-error2
-rw-r--r--_test/data/invalid-item-without-trailing-break.loader-error2
-rw-r--r--_test/data/invalid-merge-1.loader-error2
-rw-r--r--_test/data/invalid-merge-2.loader-error2
-rw-r--r--_test/data/invalid-omap-1.loader-error3
-rw-r--r--_test/data/invalid-omap-2.loader-error3
-rw-r--r--_test/data/invalid-omap-3.loader-error4
-rw-r--r--_test/data/invalid-pairs-1.loader-error3
-rw-r--r--_test/data/invalid-pairs-2.loader-error3
-rw-r--r--_test/data/invalid-pairs-3.loader-error4
-rw-r--r--_test/data/invalid-python-bytes-2-py3.loader-error2
-rw-r--r--_test/data/invalid-python-bytes-py3.loader-error2
-rw-r--r--_test/data/invalid-python-module-kind.loader-error1
-rw-r--r--_test/data/invalid-python-module-value.loader-error1
-rw-r--r--_test/data/invalid-python-module.loader-error1
-rw-r--r--_test/data/invalid-python-name-kind.loader-error1
-rw-r--r--_test/data/invalid-python-name-module-2.loader-error1
-rw-r--r--_test/data/invalid-python-name-module.loader-error1
-rw-r--r--_test/data/invalid-python-name-object.loader-error1
-rw-r--r--_test/data/invalid-python-name-value.loader-error1
-rw-r--r--_test/data/invalid-simple-key.loader-error3
-rw-r--r--_test/data/invalid-single-quote-bug.code1
-rw-r--r--_test/data/invalid-single-quote-bug.data2
-rw-r--r--_test/data/invalid-starting-character.loader-error1
-rw-r--r--_test/data/invalid-tag-1.loader-error1
-rw-r--r--_test/data/invalid-tag-2.loader-error1
-rw-r--r--_test/data/invalid-tag-directive-handle.loader-error2
-rw-r--r--_test/data/invalid-tag-directive-prefix.loader-error2
-rw-r--r--_test/data/invalid-tag-handle-1.emitter-error5
-rw-r--r--_test/data/invalid-tag-handle-1.loader-error2
-rw-r--r--_test/data/invalid-tag-handle-2.emitter-error5
-rw-r--r--_test/data/invalid-tag-handle-2.loader-error2
-rw-r--r--_test/data/invalid-uri-escapes-1.loader-error1
-rw-r--r--_test/data/invalid-uri-escapes-2.loader-error1
-rw-r--r--_test/data/invalid-uri-escapes-3.loader-error1
-rw-r--r--_test/data/invalid-uri.loader-error1
-rw-r--r--_test/data/invalid-utf8-byte.loader-error66
-rw-r--r--_test/data/invalid-utf8-byte.stream-error66
-rw-r--r--_test/data/invalid-yaml-directive-version-1.loader-error3
-rw-r--r--_test/data/invalid-yaml-directive-version-2.loader-error2
-rw-r--r--_test/data/invalid-yaml-directive-version-3.loader-error2
-rw-r--r--_test/data/invalid-yaml-directive-version-4.loader-error2
-rw-r--r--_test/data/invalid-yaml-directive-version-5.loader-error2
-rw-r--r--_test/data/invalid-yaml-directive-version-6.loader-error2
-rw-r--r--_test/data/invalid-yaml-version.loader-error2
-rw-r--r--_test/data/latin.unicode384
-rw-r--r--_test/data/mappings.events44
-rw-r--r--_test/data/merge.data1
-rw-r--r--_test/data/merge.detect1
-rw-r--r--_test/data/more-floats.code1
-rw-r--r--_test/data/more-floats.data1
-rw-r--r--_test/data/negative-float-bug.code1
-rw-r--r--_test/data/negative-float-bug.data1
-rw-r--r--_test/data/no-alias-anchor.emitter-error8
-rw-r--r--_test/data/no-alias-anchor.skip-ext0
-rw-r--r--_test/data/no-block-collection-end.loader-error3
-rw-r--r--_test/data/no-block-mapping-end-2.loader-error3
-rw-r--r--_test/data/no-block-mapping-end.loader-error1
-rw-r--r--_test/data/no-document-start.loader-error3
-rw-r--r--_test/data/no-flow-mapping-end.loader-error1
-rw-r--r--_test/data/no-flow-sequence-end.loader-error1
-rw-r--r--_test/data/no-node-1.loader-error1
-rw-r--r--_test/data/no-node-2.loader-error1
-rw-r--r--_test/data/no-tag.emitter-error5
-rw-r--r--_test/data/null.data3
-rw-r--r--_test/data/null.detect1
-rw-r--r--_test/data/odd-utf16.stream-errorbin0 -> 1311 bytes
-rw-r--r--_test/data/omap.data8
-rw-r--r--_test/data/omap.roundtrip0
-rw-r--r--_test/data/recursive-anchor.former-loader-error4
-rw-r--r--_test/data/recursive-dict.recursive3
-rw-r--r--_test/data/recursive-list.recursive2
-rw-r--r--_test/data/recursive-set.recursive7
-rw-r--r--_test/data/recursive-state.recursive2
-rw-r--r--_test/data/recursive-tuple.recursive3
-rw-r--r--_test/data/recursive.former-dumper-error3
-rw-r--r--_test/data/remove-possible-simple-key-bug.loader-error3
-rw-r--r--_test/data/resolver.data30
-rw-r--r--_test/data/resolver.path30
-rw-r--r--_test/data/run-parser-crash-bug.data8
-rw-r--r--_test/data/scalars.events28
-rw-r--r--_test/data/scan-document-end-bug.canonical3
-rw-r--r--_test/data/scan-document-end-bug.data3
-rw-r--r--_test/data/scan-line-break-bug.canonical3
-rw-r--r--_test/data/scan-line-break-bug.data3
-rw-r--r--_test/data/sequences.events81
-rw-r--r--_test/data/serializer-is-already-opened.dumper-error3
-rw-r--r--_test/data/serializer-is-closed-1.dumper-error4
-rw-r--r--_test/data/serializer-is-closed-2.dumper-error4
-rw-r--r--_test/data/serializer-is-not-opened-1.dumper-error2
-rw-r--r--_test/data/serializer-is-not-opened-2.dumper-error2
-rw-r--r--_test/data/single-dot-is-not-float-bug.code1
-rw-r--r--_test/data/single-dot-is-not-float-bug.data1
-rw-r--r--_test/data/sloppy-indentation.canonical18
-rw-r--r--_test/data/sloppy-indentation.data17
-rw-r--r--_test/data/spec-02-01.code1
-rw-r--r--_test/data/spec-02-01.data3
-rw-r--r--_test/data/spec-02-01.structure1
-rw-r--r--_test/data/spec-02-01.tokens1
-rw-r--r--_test/data/spec-02-02.data3
-rw-r--r--_test/data/spec-02-02.structure1
-rw-r--r--_test/data/spec-02-02.tokens5
-rw-r--r--_test/data/spec-02-03.data8
-rw-r--r--_test/data/spec-02-03.structure1
-rw-r--r--_test/data/spec-02-03.tokens4
-rw-r--r--_test/data/spec-02-04.data8
-rw-r--r--_test/data/spec-02-04.structure4
-rw-r--r--_test/data/spec-02-04.tokens4
-rw-r--r--_test/data/spec-02-05.data3
-rw-r--r--_test/data/spec-02-05.structure5
-rw-r--r--_test/data/spec-02-05.tokens5
-rw-r--r--_test/data/spec-02-06.data5
-rw-r--r--_test/data/spec-02-06.structure4
-rw-r--r--_test/data/spec-02-06.tokens4
-rw-r--r--_test/data/spec-02-07.data10
-rw-r--r--_test/data/spec-02-07.structure4
-rw-r--r--_test/data/spec-02-07.tokens12
-rw-r--r--_test/data/spec-02-08.data10
-rw-r--r--_test/data/spec-02-08.structure4
-rw-r--r--_test/data/spec-02-08.tokens15
-rw-r--r--_test/data/spec-02-09.data8
-rw-r--r--_test/data/spec-02-09.structure1
-rw-r--r--_test/data/spec-02-09.tokens5
-rw-r--r--_test/data/spec-02-10.data8
-rw-r--r--_test/data/spec-02-10.structure1
-rw-r--r--_test/data/spec-02-10.tokens5
-rw-r--r--_test/data/spec-02-11.code10
-rw-r--r--_test/data/spec-02-11.data9
-rw-r--r--_test/data/spec-02-11.structure4
-rw-r--r--_test/data/spec-02-11.tokens6
-rw-r--r--_test/data/spec-02-12.data8
-rw-r--r--_test/data/spec-02-12.structure5
-rw-r--r--_test/data/spec-02-12.tokens6
-rw-r--r--_test/data/spec-02-13.data4
-rw-r--r--_test/data/spec-02-13.structure1
-rw-r--r--_test/data/spec-02-13.tokens1
-rw-r--r--_test/data/spec-02-14.data4
-rw-r--r--_test/data/spec-02-14.structure1
-rw-r--r--_test/data/spec-02-14.tokens1
-rw-r--r--_test/data/spec-02-15.data8
-rw-r--r--_test/data/spec-02-15.structure1
-rw-r--r--_test/data/spec-02-15.tokens1
-rw-r--r--_test/data/spec-02-16.data7
-rw-r--r--_test/data/spec-02-16.structure1
-rw-r--r--_test/data/spec-02-16.tokens5
-rw-r--r--_test/data/spec-02-17.data7
-rw-r--r--_test/data/spec-02-17.structure1
-rw-r--r--_test/data/spec-02-17.tokens8
-rw-r--r--_test/data/spec-02-18.data6
-rw-r--r--_test/data/spec-02-18.structure1
-rw-r--r--_test/data/spec-02-18.tokens4
-rw-r--r--_test/data/spec-02-19.data5
-rw-r--r--_test/data/spec-02-19.structure1
-rw-r--r--_test/data/spec-02-19.tokens7
-rw-r--r--_test/data/spec-02-20.data6
-rw-r--r--_test/data/spec-02-20.structure1
-rw-r--r--_test/data/spec-02-20.tokens8
-rw-r--r--_test/data/spec-02-21.data4
-rw-r--r--_test/data/spec-02-21.structure1
-rw-r--r--_test/data/spec-02-21.tokens6
-rw-r--r--_test/data/spec-02-22.data4
-rw-r--r--_test/data/spec-02-22.structure1
-rw-r--r--_test/data/spec-02-22.tokens6
-rw-r--r--_test/data/spec-02-23.data13
-rw-r--r--_test/data/spec-02-23.structure1
-rw-r--r--_test/data/spec-02-23.tokens6
-rw-r--r--_test/data/spec-02-24.data14
-rw-r--r--_test/data/spec-02-24.structure5
-rw-r--r--_test/data/spec-02-24.tokens20
-rw-r--r--_test/data/spec-02-25.data7
-rw-r--r--_test/data/spec-02-25.structure1
-rw-r--r--_test/data/spec-02-25.tokens6
-rw-r--r--_test/data/spec-02-26.data7
-rw-r--r--_test/data/spec-02-26.structure5
-rw-r--r--_test/data/spec-02-26.tokens6
-rw-r--r--_test/data/spec-02-27.data29
-rw-r--r--_test/data/spec-02-27.structure17
-rw-r--r--_test/data/spec-02-27.tokens20
-rw-r--r--_test/data/spec-02-28.data26
-rw-r--r--_test/data/spec-02-28.structure10
-rw-r--r--_test/data/spec-02-28.tokens23
-rw-r--r--_test/data/spec-05-01-utf16be.databin0 -> 34 bytes
-rw-r--r--_test/data/spec-05-01-utf16be.empty2
-rw-r--r--_test/data/spec-05-01-utf16le.databin0 -> 34 bytes
-rw-r--r--_test/data/spec-05-01-utf16le.empty2
-rw-r--r--_test/data/spec-05-01-utf8.data1
-rw-r--r--_test/data/spec-05-01-utf8.empty2
-rw-r--r--_test/data/spec-05-02-utf16be.databin0 -> 90 bytes
-rw-r--r--_test/data/spec-05-02-utf16be.error3
-rw-r--r--_test/data/spec-05-02-utf16le.databin0 -> 90 bytes
-rw-r--r--_test/data/spec-05-02-utf16le.error3
-rw-r--r--_test/data/spec-05-02-utf8.data3
-rw-r--r--_test/data/spec-05-02-utf8.error3
-rw-r--r--_test/data/spec-05-03.canonical14
-rw-r--r--_test/data/spec-05-03.data7
-rw-r--r--_test/data/spec-05-04.canonical13
-rw-r--r--_test/data/spec-05-04.data2
-rw-r--r--_test/data/spec-05-05.data1
-rw-r--r--_test/data/spec-05-05.empty2
-rw-r--r--_test/data/spec-05-06.canonical8
-rw-r--r--_test/data/spec-05-06.data2
-rw-r--r--_test/data/spec-05-07.canonical8
-rw-r--r--_test/data/spec-05-07.data4
-rw-r--r--_test/data/spec-05-08.canonical8
-rw-r--r--_test/data/spec-05-08.data2
-rw-r--r--_test/data/spec-05-09.canonical3
-rw-r--r--_test/data/spec-05-09.data2
-rw-r--r--_test/data/spec-05-10.data2
-rw-r--r--_test/data/spec-05-10.error3
-rw-r--r--_test/data/spec-05-11.canonical6
-rw-r--r--_test/data/spec-05-11.data3
-rw-r--r--_test/data/spec-05-12.data9
-rw-r--r--_test/data/spec-05-12.error8
-rw-r--r--_test/data/spec-05-13.canonical5
-rw-r--r--_test/data/spec-05-13.data3
-rw-r--r--_test/data/spec-05-14.canonical7
-rw-r--r--_test/data/spec-05-14.data2
-rw-r--r--_test/data/spec-05-15.data3
-rw-r--r--_test/data/spec-05-15.error3
-rw-r--r--_test/data/spec-06-01.canonical15
-rw-r--r--_test/data/spec-06-01.data14
-rw-r--r--_test/data/spec-06-02.data3
-rw-r--r--_test/data/spec-06-02.empty2
-rw-r--r--_test/data/spec-06-03.canonical6
-rw-r--r--_test/data/spec-06-03.data2
-rw-r--r--_test/data/spec-06-04.canonical6
-rw-r--r--_test/data/spec-06-04.data4
-rw-r--r--_test/data/spec-06-05.canonical16
-rw-r--r--_test/data/spec-06-05.data6
-rw-r--r--_test/data/spec-06-06.canonical10
-rw-r--r--_test/data/spec-06-06.data7
-rw-r--r--_test/data/spec-06-07.canonical6
-rw-r--r--_test/data/spec-06-07.data8
-rw-r--r--_test/data/spec-06-08.canonical5
-rw-r--r--_test/data/spec-06-08.data2
-rw-r--r--_test/data/spec-07-01.canonical3
-rw-r--r--_test/data/spec-07-01.data3
-rw-r--r--_test/data/spec-07-01.skip-ext0
-rw-r--r--_test/data/spec-07-02.canonical3
-rw-r--r--_test/data/spec-07-02.data4
-rw-r--r--_test/data/spec-07-02.skip-ext0
-rw-r--r--_test/data/spec-07-03.data3
-rw-r--r--_test/data/spec-07-03.error3
-rw-r--r--_test/data/spec-07-04.canonical3
-rw-r--r--_test/data/spec-07-04.data3
-rw-r--r--_test/data/spec-07-05.data3
-rw-r--r--_test/data/spec-07-05.error4
-rw-r--r--_test/data/spec-07-06.canonical6
-rw-r--r--_test/data/spec-07-06.data5
-rw-r--r--_test/data/spec-07-07a.canonical3
-rw-r--r--_test/data/spec-07-07a.data2
-rw-r--r--_test/data/spec-07-07b.canonical3
-rw-r--r--_test/data/spec-07-07b.data4
-rw-r--r--_test/data/spec-07-08.canonical7
-rw-r--r--_test/data/spec-07-08.data9
-rw-r--r--_test/data/spec-07-09.canonical9
-rw-r--r--_test/data/spec-07-09.data11
-rw-r--r--_test/data/spec-07-10.canonical15
-rw-r--r--_test/data/spec-07-10.data11
-rw-r--r--_test/data/spec-07-11.data2
-rw-r--r--_test/data/spec-07-11.empty2
-rw-r--r--_test/data/spec-07-12a.canonical6
-rw-r--r--_test/data/spec-07-12a.data3
-rw-r--r--_test/data/spec-07-12b.canonical3
-rw-r--r--_test/data/spec-07-12b.data4
-rw-r--r--_test/data/spec-07-13.canonical9
-rw-r--r--_test/data/spec-07-13.data9
-rw-r--r--_test/data/spec-08-01.canonical8
-rw-r--r--_test/data/spec-08-01.data2
-rw-r--r--_test/data/spec-08-02.canonical8
-rw-r--r--_test/data/spec-08-02.data2
-rw-r--r--_test/data/spec-08-03.canonical6
-rw-r--r--_test/data/spec-08-03.data2
-rw-r--r--_test/data/spec-08-04.data2
-rw-r--r--_test/data/spec-08-04.error6
-rw-r--r--_test/data/spec-08-05.canonical7
-rw-r--r--_test/data/spec-08-05.data5
-rw-r--r--_test/data/spec-08-06.data5
-rw-r--r--_test/data/spec-08-06.error4
-rw-r--r--_test/data/spec-08-07.canonical8
-rw-r--r--_test/data/spec-08-07.data4
-rw-r--r--_test/data/spec-08-08.canonical15
-rw-r--r--_test/data/spec-08-08.data13
-rw-r--r--_test/data/spec-08-09.canonical21
-rw-r--r--_test/data/spec-08-09.data11
-rw-r--r--_test/data/spec-08-10.canonical23
-rw-r--r--_test/data/spec-08-10.data15
-rw-r--r--_test/data/spec-08-11.canonical8
-rw-r--r--_test/data/spec-08-11.data2
-rw-r--r--_test/data/spec-08-12.canonical10
-rw-r--r--_test/data/spec-08-12.data8
-rw-r--r--_test/data/spec-08-13.canonical10
-rw-r--r--_test/data/spec-08-13.data4
-rw-r--r--_test/data/spec-08-13.skip-ext0
-rw-r--r--_test/data/spec-08-14.canonical10
-rw-r--r--_test/data/spec-08-14.data5
-rw-r--r--_test/data/spec-08-15.canonical11
-rw-r--r--_test/data/spec-08-15.data5
-rw-r--r--_test/data/spec-09-01.canonical11
-rw-r--r--_test/data/spec-09-01.data6
-rw-r--r--_test/data/spec-09-02.canonical7
-rw-r--r--_test/data/spec-09-02.data6
-rw-r--r--_test/data/spec-09-03.canonical7
-rw-r--r--_test/data/spec-09-03.data6
-rw-r--r--_test/data/spec-09-04.canonical6
-rw-r--r--_test/data/spec-09-04.data4
-rw-r--r--_test/data/spec-09-05.canonical7
-rw-r--r--_test/data/spec-09-05.data8
-rw-r--r--_test/data/spec-09-06.canonical3
-rw-r--r--_test/data/spec-09-06.data1
-rw-r--r--_test/data/spec-09-07.canonical11
-rw-r--r--_test/data/spec-09-07.data6
-rw-r--r--_test/data/spec-09-08.canonical6
-rw-r--r--_test/data/spec-09-08.data1
-rw-r--r--_test/data/spec-09-09.canonical7
-rw-r--r--_test/data/spec-09-09.data6
-rw-r--r--_test/data/spec-09-10.canonical5
-rw-r--r--_test/data/spec-09-10.data3
-rw-r--r--_test/data/spec-09-11.canonical6
-rw-r--r--_test/data/spec-09-11.data5
-rw-r--r--_test/data/spec-09-12.canonical12
-rw-r--r--_test/data/spec-09-12.data8
-rw-r--r--_test/data/spec-09-13.canonical11
-rw-r--r--_test/data/spec-09-13.data6
-rw-r--r--_test/data/spec-09-14.data14
-rw-r--r--_test/data/spec-09-14.error6
-rw-r--r--_test/data/spec-09-15.canonical18
-rw-r--r--_test/data/spec-09-15.data13
-rw-r--r--_test/data/spec-09-16.canonical6
-rw-r--r--_test/data/spec-09-16.data3
-rw-r--r--_test/data/spec-09-17.canonical4
-rw-r--r--_test/data/spec-09-17.data3
-rw-r--r--_test/data/spec-09-18.canonical8
-rw-r--r--_test/data/spec-09-18.data9
-rw-r--r--_test/data/spec-09-19.canonical6
-rw-r--r--_test/data/spec-09-19.data4
-rw-r--r--_test/data/spec-09-20.canonical8
-rw-r--r--_test/data/spec-09-20.data11
-rw-r--r--_test/data/spec-09-20.skip-ext0
-rw-r--r--_test/data/spec-09-21.data8
-rw-r--r--_test/data/spec-09-21.error7
-rw-r--r--_test/data/spec-09-22.canonical10
-rw-r--r--_test/data/spec-09-22.data4
-rw-r--r--_test/data/spec-09-23.canonical10
-rw-r--r--_test/data/spec-09-23.data11
-rw-r--r--_test/data/spec-09-24.canonical10
-rw-r--r--_test/data/spec-09-24.data6
-rw-r--r--_test/data/spec-09-25.canonical4
-rw-r--r--_test/data/spec-09-25.data3
-rw-r--r--_test/data/spec-09-26.canonical3
-rw-r--r--_test/data/spec-09-26.data8
-rw-r--r--_test/data/spec-09-27.canonical3
-rw-r--r--_test/data/spec-09-27.data8
-rw-r--r--_test/data/spec-09-28.canonical3
-rw-r--r--_test/data/spec-09-28.data8
-rw-r--r--_test/data/spec-09-29.canonical4
-rw-r--r--_test/data/spec-09-29.data4
-rw-r--r--_test/data/spec-09-30.canonical7
-rw-r--r--_test/data/spec-09-30.data14
-rw-r--r--_test/data/spec-09-31.canonical7
-rw-r--r--_test/data/spec-09-31.data14
-rw-r--r--_test/data/spec-09-32.canonical7
-rw-r--r--_test/data/spec-09-32.data14
-rw-r--r--_test/data/spec-09-33.canonical7
-rw-r--r--_test/data/spec-09-33.data14
-rw-r--r--_test/data/spec-10-01.canonical12
-rw-r--r--_test/data/spec-10-01.data2
-rw-r--r--_test/data/spec-10-02.canonical14
-rw-r--r--_test/data/spec-10-02.data8
-rw-r--r--_test/data/spec-10-03.canonical12
-rw-r--r--_test/data/spec-10-03.data4
-rw-r--r--_test/data/spec-10-04.canonical11
-rw-r--r--_test/data/spec-10-04.data4
-rw-r--r--_test/data/spec-10-05.canonical14
-rw-r--r--_test/data/spec-10-05.data7
-rw-r--r--_test/data/spec-10-06.canonical16
-rw-r--r--_test/data/spec-10-06.data2
-rw-r--r--_test/data/spec-10-07.canonical16
-rw-r--r--_test/data/spec-10-07.data7
-rw-r--r--_test/data/spec-10-08.data5
-rw-r--r--_test/data/spec-10-08.error5
-rw-r--r--_test/data/spec-10-09.canonical8
-rw-r--r--_test/data/spec-10-09.data4
-rw-r--r--_test/data/spec-10-10.canonical16
-rw-r--r--_test/data/spec-10-10.data8
-rw-r--r--_test/data/spec-10-11.canonical24
-rw-r--r--_test/data/spec-10-11.data7
-rw-r--r--_test/data/spec-10-12.canonical9
-rw-r--r--_test/data/spec-10-12.data3
-rw-r--r--_test/data/spec-10-13.canonical11
-rw-r--r--_test/data/spec-10-13.data5
-rw-r--r--_test/data/spec-10-14.canonical11
-rw-r--r--_test/data/spec-10-14.data4
-rw-r--r--_test/data/spec-10-15.canonical18
-rw-r--r--_test/data/spec-10-15.data3
-rw-r--r--_test/data/str.data1
-rw-r--r--_test/data/str.detect1
-rw-r--r--_test/data/tags.events12
-rw-r--r--_test/data/test_mark.marks38
-rw-r--r--_test/data/timestamp-bugs.code8
-rw-r--r--_test/data/timestamp-bugs.data6
-rw-r--r--_test/data/timestamp.data5
-rw-r--r--_test/data/timestamp.detect1
-rw-r--r--_test/data/unclosed-bracket.loader-error6
-rw-r--r--_test/data/unclosed-quoted-scalar.loader-error2
-rw-r--r--_test/data/undefined-anchor.loader-error3
-rw-r--r--_test/data/undefined-constructor.loader-error1
-rw-r--r--_test/data/undefined-tag-handle.loader-error1
-rw-r--r--_test/data/unknown.dumper-error1
-rw-r--r--_test/data/unsupported-version.emitter-error5
-rw-r--r--_test/data/utf16be.code1
-rw-r--r--_test/data/utf16be.databin0 -> 30 bytes
-rw-r--r--_test/data/utf16le.code1
-rw-r--r--_test/data/utf16le.databin0 -> 30 bytes
-rw-r--r--_test/data/utf8-implicit.code1
-rw-r--r--_test/data/utf8-implicit.data1
-rw-r--r--_test/data/utf8.code1
-rw-r--r--_test/data/utf8.data1
-rw-r--r--_test/data/util/00_ok.yaml3
-rw-r--r--_test/data/util/01_second_rt_ok.yaml3
-rw-r--r--_test/data/util/02_not_ok.yaml2
-rw-r--r--_test/data/util/03_no_comment_ok.yaml2
-rw-r--r--_test/data/valid_escape_characters.code1
-rw-r--r--_test/data/valid_escape_characters.data1
-rw-r--r--_test/data/valid_escape_characters.skip-ext0
-rw-r--r--_test/data/value.data1
-rw-r--r--_test/data/value.detect1
-rw-r--r--_test/data/yaml.data3
-rw-r--r--_test/data/yaml.detect1
-rw-r--r--_test/lib/canonical.py388
-rw-r--r--_test/lib/test_all.py20
-rw-r--r--_test/lib/test_appliance.py209
-rw-r--r--_test/lib/test_build.py16
-rw-r--r--_test/lib/test_build_ext.py17
-rw-r--r--_test/lib/test_canonical.py51
-rw-r--r--_test/lib/test_constructor.py372
-rw-r--r--_test/lib/test_emitter.py132
-rw-r--r--_test/lib/test_errors.py92
-rw-r--r--_test/lib/test_input_output.py180
-rw-r--r--_test/lib/test_mark.py35
-rw-r--r--_test/lib/test_reader.py44
-rw-r--r--_test/lib/test_recursive.py58
-rw-r--r--_test/lib/test_representer.py51
-rw-r--r--_test/lib/test_resolver.py111
-rw-r--r--_test/lib/test_structure.py225
-rw-r--r--_test/lib/test_tokens.py86
-rw-r--r--_test/lib/test_yaml.py20
-rw-r--r--_test/lib/test_yaml_ext.py403
-rw-r--r--_test/roundtrip.py362
-rw-r--r--_test/test_a_dedent.py49
-rw-r--r--_test/test_add_xxx.py188
-rw-r--r--_test/test_anchor.py540
-rw-r--r--_test/test_api_change.py239
-rw-r--r--_test/test_class_register.py145
-rw-r--r--_test/test_collections.py20
-rw-r--r--_test/test_comment_manipulation.py588
-rw-r--r--_test/test_comments.py855
-rw-r--r--_test/test_contextmanager.py117
-rw-r--r--_test/test_copy.py117
-rw-r--r--_test/test_cyaml.py86
-rw-r--r--_test/test_dataclass.py180
-rw-r--r--_test/test_datetime.py193
-rw-r--r--_test/test_deprecation.py129
-rw-r--r--_test/test_docinfo.py44
-rw-r--r--_test/test_documents.py105
-rw-r--r--_test/test_fail.py233
-rw-r--r--_test/test_float.py85
-rw-r--r--_test/test_flowsequencekey.py25
-rw-r--r--_test/test_indentation.py335
-rw-r--r--_test/test_int.py32
-rw-r--r--_test/test_issues.py1205
-rw-r--r--_test/test_json_numbers.py58
-rw-r--r--_test/test_line_col.py92
-rw-r--r--_test/test_literal.py361
-rw-r--r--_test/test_none.py41
-rw-r--r--_test/test_numpy.py24
-rw-r--r--_test/test_program_config.py59
-rw-r--r--_test/test_spec_examples.py293
-rw-r--r--_test/test_string.py213
-rw-r--r--_test/test_tag.py219
-rw-r--r--_test/test_version.py180
-rw-r--r--_test/test_yamlfile.py233
-rw-r--r--_test/test_yamlobject.py92
-rw-r--r--_test/test_z_check_debug_leftovers.py36
-rw-r--r--_test/test_z_data.py404
-rw-r--r--_test/test_z_olddata.py42
-rw-r--r--anchor.py6
-rw-r--r--comments.py6
-rw-r--r--compat.py27
-rw-r--r--composer.py6
-rw-r--r--configobjwalker.py6
-rw-r--r--constructor.py32
-rw-r--r--cyaml.py8
-rw-r--r--docinfo.py91
-rw-r--r--dumper.py8
-rw-r--r--emitter.py8
-rw-r--r--error.py69
-rw-r--r--events.py6
-rw-r--r--loader.py8
-rw-r--r--main.py10
-rw-r--r--nodes.py6
-rw-r--r--parser.py6
-rw-r--r--pyproject.toml4
-rw-r--r--reader.py6
-rw-r--r--representer.py15
-rw-r--r--resolver.py8
-rw-r--r--ruamel.yaml.egg-info/PKG-INFO430
-rw-r--r--ruamel.yaml.egg-info/SOURCES.txt44
-rw-r--r--ruamel.yaml.egg-info/dependency_links.txt1
-rw-r--r--ruamel.yaml.egg-info/not-zip-safe1
-rw-r--r--ruamel.yaml.egg-info/requires.txt10
-rw-r--r--ruamel.yaml.egg-info/top_level.txt1
-rw-r--r--scalarbool.py6
-rw-r--r--scalarfloat.py6
-rw-r--r--scalarint.py6
-rw-r--r--scalarstring.py8
-rw-r--r--scanner.py92
-rw-r--r--serializer.py8
-rw-r--r--setup.cfg4
-rw-r--r--tag.py6
-rw-r--r--timestamp.py10
-rw-r--r--tokens.py6
-rwxr-xr-xtox.ini49
-rw-r--r--util.py26
690 files changed, 17254 insertions, 1094 deletions
diff --git a/.hgignore b/.hgignore
new file mode 100644
index 0000000..4408f71
--- /dev/null
+++ b/.hgignore
@@ -0,0 +1,20 @@
+# this should only include project specific files. Ignores that are valid for other
+# ruamel. projects like e.g. the directory .tox should go in the file pointed to by
+# the ui->ignore entry in ~/.hgrc (mercurial doesn't conform to the XDG Base Directory
+# Specification):
+# [ui]
+# ignore = ~/.hgext/hgignore
+
+syntax: glob
+
+# _yaml.so
+venv
+TODO.rst
+try_*
+_doc/*.pdf
+_doc/*.html
+_doc/*.rst
+_doc/*.md
+*.py_alt
+
+ziglib
diff --git a/.hgtags b/.hgtags
new file mode 100644
index 0000000..5836cb7
--- /dev/null
+++ b/.hgtags
@@ -0,0 +1,234 @@
+8feb415618c6a4c5583e8732d710dcebb5eaf4f2 0.10.5
+547ffc9bcc4321968a3c59b32ca0acfeba40b9b0 0.10.15
+70d15108c0fedd81c159315c78c7d68f7a2fd9d7 0.10.16
+a79ba7f0cc8fa5d68518893a3f10e17676e67046 0.10.17
+569743fc9f63e33379425734c8f8db5d8db2511d 0.10.18
+71814e979983a45be5feea16815becba232593e1 0.10.19
+74915d2345dfde360479b94a5975da36711b4d6e 0.10.20
+c78d15d315599670be54141ca8c96e050fabca2e 0.10.22
+e18897e2dfc1fbd0f4949675390804b7c5cf9177 0.10.23
+302116269a34dfbe94d9d38ea3d51afe3651d45e 0.11.0
+37f1b713fec62c079a49135771be21a7a204ca84 0.11.1
+a1efab2dfcc7f3e1be345715e0363c6ae99c6bf9 0.11.2
+447f5942c2fc26fbe8c6c7fe525976bb75854876 0.11.3
+b6ea93b67e983fd38fc3835b723aca5edc4e94b8 0.11.4
+89d31eebace0c036541c1f23d36450106e5b78cb 0.11.5
+1416683886f050f4475355ae026c07f1d8553759 0.11.6
+417dcdd367bad9a6b0e19ff9088fd8f933c544df 0.11.7
+2309d082da2d8ffca483112089223f0b77c6876e 0.11.8
+12756d40a6d4cf21af22e125ea019c32a94282b1 0.11.9
+0574cc19d4ca1d123ef45d09e4b879daab2f2c46 0.11.10
+9870512ae409df49e9528954a072a9772e68125f 0.11.11
+9d266b434de53721015b2263b51ff5620dcdd9b4 0.11.12
+1e3e1113a711a180ec4ba8d90864a9b10d2ffdb8 0.11.13
+63986ab6f97004ccb79984ec571d6e96ea19c499 0.11.14
+638e2c7b68f3996243c02fd85e76e988d83489ec 0.11.15
+6eca2e8f28dc2ed64f065972327be5a0e381549d 0.12.0
+1ffda64d4afc628b873f093bf17802bf54e95121 0.12.1
+250036e446aeef39ed5d5a3aa2d9a757b636e026 0.12.2
+08ce749d7cd74d7e98c23376e4a477991327a85d 0.12.3
+20ef4712ac85f32ff31ed9aaeac3c40991313547 0.12.4
+67b8826a8e42a2f2b086cd63d746fa6b242c4ccb 0.12.5
+2db72ff01ca15a918d0cb9ca2b6d4c67d2834061 0.12.6
+2863a2e12e41f0458a9f142b45de6f92cf69d08b 0.12.7
+f7480c40fbccdb7f081710f382c4f2bf98b0b8ec 0.12.8
+1038e7c5ed1c3ddee664e16302b322ee16be1dfa 0.12.9
+2b85c4e3233dd9b1434908667bc522e5e7723946 0.12.10
+a15c5188b4772c859ea9f2daf6fcafb90d7decf7 0.12.11
+bf2cb0dfa0bb51e1af44eec03657c31972b96d9a 0.12.12
+dfb987e8568ce188a91d9cdf8c6d06fc0ef1d0f1 0.12.13
+171c3653fc01ba5b29edacb7bb6465c46a99b82b 0.12.14
+3209511e8e8e0b2fcce9952a73067978dc707ecc 0.12.15
+2ecd2e567070bab1a40ea0f8708c44823ef6df5a 0.12.16
+ab15a86e854bcc82cbafd64add8f42d8b4192ff1 0.12.17
+dfccdfc4cb619b69033409934536d3c739254ce1 0.12.18
+3069527a2a53c27d74d54bac053674d300963378 0.13.0
+89a15d2e8e928767f1a69b480203f2bc685ea31d 0.13.1
+9f28f346d6f4749f8ef9b50eb02493673379c7f5 0.13.2
+c06c6126044670169a0dc69535271b85e49154ba 0.13.3
+c796c14d4b4e61a6ab62b25407128228c3a1ac05 0.13.4
+df484f2f5ebe6d442ebd40890ffc0e9370b98ac7 0.13.5
+a116d722726985d4ffa4fa5948130432aefaea56 0.13.6
+00c7352d22d00307ffa233b21d8a01a6ed6db4d0 0.13.7
+b9d23d3c3cb585fad55b2179f3a0f86abfaa663b 0.13.8
+86bc61249445e2fd2866fd49a771cb3545496dd3 0.13.9
+0af16314cc1d296020225d2b0f1358a095bbac2b 0.13.10
+c7329c96fd3b8230a1dd816e898a15db40d23266 0.13.11
+9ba2653954062f65220c13f494ba3876cfbc7595 0.13.12
+113e4054a46c8900418c5d0c0a68b7da36767032 0.13.13
+f29921d2048ec07feb28224483f228e241227a34 0.13.14
+faf223fc6bdda358c22f3382e187cbaa32cc502b 0.14.0
+a8be524fc8e632bdc416c46fb1bc1de3cda55099 0.14.1
+6cc126e7015ed44662fa33509ce3b0e53397ee6f 0.14.2
+b503b1654d14655ecb499c3e8093b9cf50ca2c5a 0.14.3
+a1ad3aa831df6d6ffe17e5b37c29af35c831aea1 0.14.4
+b6a58276bc87cf49861aa8bcb831eabfc1b72d95 0.14.5
+022faf1b88ba3da31ea875cc442d8336ede92817 0.14.6
+49d1bc30700e43eb3dfca1bc9cb33f5ef2858ffc 0.14.7
+f16cf2dfb253e7545e33b8f6c5c3e661935954aa 0.14.8
+b73edf12e0dc4809e0a4a9cac28c77015b8ddda6 0.14.9
+ab4f11b0bfad05427a2790610b2244931c85ff3c 0.14.10
+7d6d9a11479ab25db140f4ab26a58030fe1ae9a8 0.14.11
+739f7622c82944e5b442fec2f311da63c91d717b 0.14.12
+a26e48be0a08217f86df73e53344fc21a57c70db 0.15.0
+1cf008cc07bf56b72415c37e1f4b48579da66b70 0.15.1
+f3c06faabd8cf17b1af3892f92fc4d6127f47d41 0.15.2
+799d0d4934617ce4bfdd216f465cfdd722a4851c 0.15.3
+0be7d3cb8449b15d9ac9b097322f09e52b92f868 0.15.4
+64a5108dff8e4326ced091a4577847fe7a605f0b 0.15.5
+a4a4e3d245ac5e1dd018d26fef98cc7d84048070 0.15.6
+e27044ee0ee074a6d8d11129a909b0c5b488a782 0.15.7
+9d5b1d066c7330bfc39d58d321e65ecf0668935c 0.15.8
+dc96733bf4536b7b6e2ba199bd85c5db7e337680 0.15.9
+17aa117ae832e5483e0416ec04eb7204ec61ea28 0.15.10
+31f8f2bf525ffbced6a0e36a388a15b7028e7b1f 0.15.11
+151fa75a6d4a8beb32d9ed645ee55e1350014d95 0.15.12
+b5ba61f0e2ecc6f3c2d4480870ce2939fd5ca3d3 0.15.13
+ec277e627677a44a038d625cb16c06f6ee7a948f 0.15.14
+0527f4b3d13f8431da0b01593dd41924f7ff8a99 0.15.15
+bf3cd222269ea52936fe5d409da06b835a20fedf 0.15.16
+d50dbb22c395a8ac405b33fc1224115486ca6bb3 0.15.17
+26a3f3c3899e472f73c0ed3b497f768ba31fe54b 0.15.18
+75514470f9d04828d7c7344a9fd3b739ffb5b80e 0.15.19
+b73bd8c027c270b954689f55efb5ca7ef5211d20 0.15.20
+d763e6293909c627d54f3d0bbb362dee6adf6042 0.15.21
+72467bb7d686b011acbb60a195a57cc71112f1ff 0.15.22
+4cda40ba2bc0c10f359996dec560512ba505024b 0.15.23
+bef1b41eccd05be8101eb1195ac39748e286fded 0.15.24
+dcd4e26416d3bea46074ef5c44495c86efcb3a2b 0.15.25
+d3d770042d8255fb51975176dbb874efeeaa3aa3 0.15.26
+f4fe11e61abbaae7ca9889c9123ec508c4c913ad 0.15.27
+aa1cd8e567df4606f23526c69a0724bd59f63ab3 0.15.28
+f12c043142470687c08e133c74b14b9404b783a3 0.15.29
+a173fbba4276d0ceec54ffe7154af7a3d39a0f1d 0.15.30
+2c27e4a45fa1dd08ef4d89ef2362b514aa730c66 0.15.31
+dc56cc527bd6e58f486dd768d06e320c3599cd25 0.15.32
+50d40e5740cc51ae0c6fb14b8cf1bfe15f352bf9 0.15.33
+ea4d54f8e034128c38fcba4a7ed0f606585333dc 0.15.34
+5722d2358423abe76b8247aef1c6358e1280c32a 0.15.35
+e7161bfd51eebb3d0e6efc9f37e4cbe56615107c 0.15.36
+731f9a604926831c4ed5388e76556f683d63d1c0 0.15.37
+11b67cf111872e59c91b86b7b65e7e6361b26824 0.15.38
+6661f6fd07ef871a365412c8df2d4ada136473b6 0.15.39
+df8ae62f51f4489e2b28887f80893088f41f3c6b 0.15.40
+75b1b39341d9c3c14c06aafe0c51ee7153db5349 0.15.41
+6d1b19ab0d126fa65bf4e1e734de7f04474df096 0.15.42
+2dd2b6fabe10064477715b5e497c620c96b16d04 0.15.43
+984524322dd6ca8e696b109d4b49a39b45348319 0.15.44
+755e626e50daea821cca9b837a29bab8bec65bac 0.15.45
+1da5225301efae0f81cc93b6064e08ef58863dfe 0.15.46
+b65df6fde9e802abe64aebab3f01bb4ed4a610c8 0.15.47
+7b02389f82e984423f3920e715ab34064a51fbd5 0.15.48
+ba539f8f4b02af1d3a5993e2b568c57c474e83ba 0.15.49
+7aff374b37a49e7669694deed67d0267d2a934b1 0.15.50
+26ef3be1c51a06ca2819e087d12afb3f0581d0fd 0.15.51
+ace40a6a40270b75320971d1c9ff7625fbf6e081 0.15.52
+c1a5b02fdf77f45b78e99469b24da26074249a31 0.15.53
+0b00c98d86f0c6000b9ca294bde50c89aeab8f58 0.15.54
+11f34d8d5dba6014b93be1232c48b7cfbe17ca7e 0.15.55
+5e371ab3345ca4f8b966e0cb7d00d69b94b1a1e4 0.15.56
+befe925d0080bc0c98fc33855dae78620686d9e1 0.15.57
+c98b054e7b4bf78745eb585f614b85a4febb21d6 0.15.58
+ed53aca288aadd5e846a7995064b6b1ec10a2948 0.15.59
+8bd3d99acd97d1306617309d9ed3e18711f0e57d 0.15.60
+7785d0275147a34f8bab3f2f1c6ee8cc56946745 0.15.61
+84a177710530e25cc31dc99542afba10ddf78c2c 0.15.62
+b62ff264208e712ae783e7ad0fa3daaebf8be07b 0.15.63
+fb23f823f2bedf6416fe116b35a8342304cb4723 0.15.64
+8a949259a8ea28289e3c46a17870fffafac607c0 0.15.65
+8fbe5da09aa21d08c82efccf23ab7bef52b26abb 0.15.66
+bf41e2157c0fcb543f9ec7b2147178c83e588190 0.15.67
+a7a8cc1882ffdc96aa6bef1233a3adcc6d658e68 0.15.68
+5426db7ee3ac3a36a5de30366ea848b3ac502656 0.15.69
+9f203c29fc784913fd1e637d111309b69e7f4234 0.15.70
+3f910bde276c400ec8a1954df83f7e48388d0e4d 0.15.71
+f103b8fab8f632088ac88f9c51d4f34c3816a4d2 0.15.72
+10764fa934a1df35a7b561723cdddac19bf0bdab 0.15.73
+053b79d9006c85da53b87791fa7a16d55c8d6315 0.15.74
+4d28cc83903ad3e68f245076cd785be66ce16251 0.15.75
+7947491ccc5a4b4e505617a557fc387b0ade0b62 0.15.76
+15615199dba043d6590e22502b2ab93962bd1c2c 0.15.77
+c4f22d89a9e22cb5b8c75daaa5c655fae975c84a 0.15.78
+8114a8b4db672d29a72313729875f2bb46db3bc8 0.15.79
+16041dd1f88882dae7d240a17aab0b2a90592cf8 0.15.80
+e96ce32ebd9bf49887aee0cc0af08db3d195f421 0.15.81
+48491562733bf8b1b37d41748218452841c0ab35 0.15.82
+32518f0a4636ed1e9078f364c281af885909a696 0.15.83
+b0872a7b06ad816cbd51a654a552c9a70df7fbce 0.15.84
+47646c03f53a1ab1bef97c8b15fd0fc99f39b4f1 0.15.85
+45509d2e52c1f891e5167268f1b3fd2f656380ac 0.15.86
+9a738d6f4b707b3a14683747e5daa380601944bc 0.15.87
+048a24402ba2fb258549eaf3fbda849b8bb131b0 0.15.88
+0419584733025ae91a8cec04915b6cee5c43b184 0.15.89
+6ca00e22cd343eb39c5025126ed18fcf821a413b 0.15.90
+22286ce99d6552c658cb11d0a8a3c9b962ebe510 0.15.91
+939d1a112a04b14478f77be9a11e14800f5cc186 0.15.92
+81c296f3afb1fe39915ef04b782ec285abf1032d 0.15.93
+3d30d3cf745c4a3799284418e4ed928bcf340f38 0.15.94
+b2827bd4175ed02d03395447ca7a45f2db32b331 0.15.95
+572b7bdf491d897f309442b00a23297a84c1c611 0.15.96
+6273eafdbcec62ddb9825b13ddb3c870f7753860 0.15.97
+c7006e510469f8c1cb2360d769d17ea6f0adedb6 0.15.98
+25737c624a8ca99faacbf4a2d18c58a074f158c6 0.15.99
+af9628b0d0479c35a60efc44b8d6dff9dc95aed2 0.15.100
+9bad19a3c13e932430a14a3bd703af9ca21bede4 0.16.0
+838437a06b40d7d1056f899a7eebb2f8bff6297f 0.16.1
+8f30eb53eec169e3788ac6ebc518b2bb3304f672 0.16.2
+28ae1667ac5be81022313f1c46d30ff3baeb90fd 0.16.3
+7869db1695cc3da9a3d321c469c21872b32dcd51 0.16.4
+ff02b83b8f9127ca8e7fea3aaa15d0fa758dc0be 0.16.5
+44504659794e463523ff8d0b40bac18dfe3b52f8 0.16.6
+8d05bcae7e91224ba77689671845d36524d33cc9 0.16.7
+0228e1a734b571530f9abca9b45c4da248cc4272 0.16.8
+17f2c2782abba9b4193fb293dbcad64451ee4bd7 0.16.9
+42b89940bc91d2a9c6250e9e8d2fb0f8b37155e2 0.16.10
+43b0288519407853045c1594c831d13c623766ae 0.16.11
+53ec5198e50a3c4c876ca3f495905e6c1efc1bed 0.16.12
+ce172fcf4b6174cc1453762cda2796bd5b28d21a 0.16.13
+7c1c4a4e17cfc46eb86874fb4672c2666638fc2e 0.17.0
+961613d5ea1abba596d01491ece2a538c0556e09 0.17.1
+a8a9141e7a9002037fb7469b9e4c4659c90e9322 0.17.2
+f115bb2a18a054b755dfa595e4fd7c61d6a22029 0.17.3
+eb1a5e34fc549a1926a935a0bf9fd50fe25b2e08 0.17.4
+c297e855326a83d7189cbf16de21d56b6085e0f1 0.17.5
+e1c74d1ecced1bb9b1096214e0c31470b2208518 0.17.6
+1838279236c81a5900fb5247f5517a5e1347d30a 0.17.7
+33f00b1e0cfb6f2ff027ec44a5233640c3ca6643 0.17.8
+58889c2d944d5d0b22948a15d6fcb97c68d599de 0.17.9
+b5ca30ca77858b6624b21fff0770c82aa9db15a6 0.17.10
+a73e6ac42aafe12ca29f83a30b9546f30c83c7f5 0.17.11
+7b39c91368c599c53fabf8887c7e5cb4bac84582 0.17.12
+98402839fd1b9ebc6633856379f39ca502a4de05 0.17.13
+b1ba34b632eb6355e32beefbbd022aa21ed9aac1 0.17.14
+619c54d398bf5d78070fcd2523da938c673df700 0.17.15
+c446ce20b4e577a5376dc84a5104fbdb3c61e2b1 0.17.16
+3feb83757f9ca10f002c19d570e7a124128d3773 0.17.17
+d7242a1682713ab3940016fb144abaaecc260ceb 0.17.18
+1fa1bc2b408ed48b118b9789f5f0bfbb4604e96d 0.17.19
+8fd327e249b42eeccff6702373ed7a4127943dfa 0.17.20
+c887dfe19e2f01b770a2b4b04163a01e00a33f85 0.17.21
+56b3e2666fb275deab3eec99193c103e4edf93bb 0.17.22
+b4c00a90b4d003ff3f239df622fb638cd33146dd 0.17.23
+4309006902d2453399588f4ddccfb3fc460e1eba 0.17.24
+12a642699fa84085248317ee765c4956f6deeec7 0.17.25
+8a26dc2a156aa189c472b5efeb10e8c3de206091 0.17.26
+f76dde33e9a175e7505a2933a5c2423d4e3db9aa 0.17.27
+d522a02977979e5feef1d0f1b94b6b7f823c0bdd 0.17.28
+41fd3925691106c999959771e54bd69cce70d1c8 0.17.29
+0ed43732b9e309d397e9c9cfa74f115f40f51a6b 0.17.30
+b01b2f5c1ff21d561193de37b7e843baa2b91c91 0.17.31
+e2d9ee085ae698c4dca733b83a718b9e4a6ec8c0 0.17.32
+c4b4f7766d1845b67606fba5dd7d753e614417ea 0.17.33
+31cd820ffa1ae47947343603617be8294d8f1657 0.17.34
+2088c8ea914f2626e8dc1da8ef7b43404c69a7ee 0.17.35
+c454afd9526435757eb797c6afedc6a308a139b7 0.17.36
+d8202d5a430253db5393dc52048db0f00fc8a595 0.17.37
+a255af9575de872dae9c19825beccd6f288a215a 0.17.38
+5ee159ab1327c416b94ffd065018c526e518e19b 0.17.39
+c87a704e4aeabc56009de61872b97db39f4d1a72 0.17.40
+3a3b56f6bf0a8944a399c58a9ca430330afadbe3 0.18.0
+c0da0ba934877fdfe63bee77ec12a7d2341f5398 0.18.1
+a35908655d678b8463ee6198869a0708b3446e06 0.18.2
+e32fbfcda1a48d808542670d91f1e84d14f69956 0.18.3
+08d87cada1f6e5fedde079b55536061e4fe246a0 0.18.4
+eb3ecf31085135283908fc8449befebbc1fff4b3 0.18.5
diff --git a/.readthedocs.yaml b/.readthedocs.yaml
new file mode 100644
index 0000000..ebafea4
--- /dev/null
+++ b/.readthedocs.yaml
@@ -0,0 +1,20 @@
+version: 2
+
+build:
+ os: ubuntu-22.04
+ tools:
+ python: "3.11"
+ jobs:
+ pre_build:
+ - pip install ryd>=0.9.2
+ - ryd --version -v
+ - ryd convert --generate-mkdocs-config mkdocs.yaml _doc
+
+python:
+ install:
+ - method: pip
+ path: .
+ extra_requirements: [docs]
+
+mkdocs:
+ configuration: mkdocs.yaml
diff --git a/CHANGES b/CHANGES
index 2fc13eb..de0d020 100644
--- a/CHANGES
+++ b/CHANGES
@@ -1,3 +1,10 @@
+[0.18.6, 2024-02-07]:
+- fixed an issue with dataclass loading when the fields were collections (bug found
+ as a result of a question by [FibroMyAlgebra](https://stackoverflow.com/users/6855070/fibromyalgebra)
+ on [StackOverflow](https://stackoverflow.com/a/77485786/1307905))
+- fixed an issue loading dataclasses with `InitVar` fields when `from __future__ import
+ annotations` was used to delay evaluation of typing.
+
[0.18.5, 2023-11-03]:
- there is some indication that dependent packages have been pinned to use specific
(tested) and just install the latest even in Python versions that have end-of-life
diff --git a/LICENSE b/LICENSE
index 5fdca40..8777c9c 100644
--- a/LICENSE
+++ b/LICENSE
@@ -1,6 +1,6 @@
The MIT License (MIT)
- Copyright (c) 2014-2023 Anthon van der Neut, Ruamel bvba
+ Copyright (c) 2014-2024 Anthon van der Neut, Ruamel bvba
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
diff --git a/PKG-INFO b/PKG-INFO
deleted file mode 100644
index b4e9d9e..0000000
--- a/PKG-INFO
+++ /dev/null
@@ -1,430 +0,0 @@
-Metadata-Version: 2.1
-Name: ruamel.yaml
-Version: 0.18.5
-Summary: ruamel.yaml is a YAML parser/emitter that supports roundtrip preservation of comments, seq/map flow style, and map key order
-Author: Anthon van der Neut
-Author-email: a.van.der.neut@ruamel.eu
-License: MIT license
-Project-URL: Home, https://sourceforge.net/p/ruamel-yaml/
-Project-URL: Source, https://sourceforge.net/p/ruamel-yaml/code/ci/default/tree/
-Project-URL: Tracker, https://sourceforge.net/p/ruamel-yaml/tickets/
-Project-URL: Documentation, https://yaml.readthedocs.io/
-Keywords: yaml 1.2 parser round-trip preserve quotes order config
-Classifier: Development Status :: 4 - Beta
-Classifier: Intended Audience :: Developers
-Classifier: License :: OSI Approved :: MIT License
-Classifier: Operating System :: OS Independent
-Classifier: Programming Language :: Python
-Classifier: Programming Language :: Python :: 3 :: Only
-Classifier: Programming Language :: Python :: 3.10
-Classifier: Programming Language :: Python :: 3.11
-Classifier: Programming Language :: Python :: 3.12
-Classifier: Programming Language :: Python :: 3.7
-Classifier: Programming Language :: Python :: 3.8
-Classifier: Programming Language :: Python :: 3.9
-Classifier: Programming Language :: Python :: Implementation :: CPython
-Classifier: Topic :: Software Development :: Libraries :: Python Modules
-Classifier: Topic :: Text Processing :: Markup
-Classifier: Typing :: Typed
-Requires-Python: >=3.7
-Description-Content-Type: text/markdown; charset=UTF-8; variant=CommonMark
-Provides-Extra: jinja2
-Provides-Extra: docs
-License-File: LICENSE
-
-# ruamel.yaml
-
-`ruamel.yaml` is a YAML 1.2 loader/dumper package for Python.
-<table class="docutils">
- <tr> <td>version</td>
- <td>0.18.5</td>
- </tr>
- <tr> <td>updated</td>
- <td>2023-11-03</td>
- </tr>
- <tr> <td>documentation</td>
- <td><a href="https://yaml.readthedocs.io">https://yaml.readthedocs.io</a></td>
- </tr>
- <tr> <td>repository</td>
- <td><a href="https://sourceforge.net/projects/ruamel-yaml">https://sourceforge.net/projects/ruamel-yaml</a></td>
- </tr>
- <tr> <td>pypi</td>
- <td><a href="https://pypi.org/project/ruamel.yaml">https://pypi.org/project/ruamel.yaml</a></td>
- </tr>
-</table>
-
-As announced, in 0.18.0, the old PyYAML functions have been deprecated.
-(`scan`, `parse`, `compose`, `load`, `emit`, `serialize`, `dump` and their variants
-(`_all`, `safe_`, `round_trip_`, etc)). If you only read this after your program has
-stopped working: I am sorry to hear that, but that also means you, or the person
-developing your program, has not tested with warnings on (which is the recommendation
-in PEP 565, and e.g. defaultin when using `pytest`). If you have troubles, explicitly use
-```
-pip install "ruamel.yaml<0.18.0"
-```
-or put something to that effects in your requirments, to give yourself
-some time to solve the issue.
-
-There will be at least one more potentially breaking change in the 0.18 series: `YAML(typ='unsafe')`
-now has a pending deprecation warning and is going to be deprecated, probably before the end of 2023.
-If you only use it to dump, please use the new `YAML(typ='full')`, the result of that can be *safely*
-loaded with a default instance `YAML()`, as that will get you inspectable, tagged, scalars, instead of
-executed Python functions/classes. (You should probably add constructors for what you actually need,
-but I do consider adding a `ruamel.yaml.unsafe` package that will re-add the `typ='unsafe'` option.
-*Please adjust/pin your dependencies accordingly if necessary.*
-
-
-There seems to be a CVE on `ruamel.yaml`, stating that the `load()` function could be abused
-because of unchecked input. `load()` was never the default function (that was `round_trip_load()`
-before the new API came into existence`. So the creator of that CVE was ill informed and
-probably lazily assumed that since `ruamel.yaml` is a derivative of PyYAML (for which
-a similar CVE exists), the same problem would still exist, without checking.
-So the CVE was always inappriate, now just more so, as the call
-to the function `load()` with any input will terminate your program with an error message. If you
-(have to) care about such things as this CVE, my recommendation is to stop using Python
-completely, as `pickle.load()` can be abused in the same way as `load()` (and like unlike `load()`
-is only documented to be unsafe, without development-time warning.
-
-Version 0.17.21 was the last one tested to be working on Python 3.5 and 3.6<BR>
-The 0.16.13 release was the last that was tested to be working on Python 2.7.
-
-
-There are two extra plug-in packages
-(`ruamel.yaml.bytes` and `ruamel.yaml.string`)
-for those not wanting to do the streaming to a
-`io.BytesIO/StringIO` buffer themselves.
-
-If your package uses `ruamel.yaml` and is not listed on PyPI, drop me an
-email, preferably with some information on how you use the package (or a
-link to the repository) and I'll keep you informed when the status of
-the API is stable enough to make the transition.
-
-<pre>
- <a href="overview/#overview">Overview</a>
-
- <a href="install/#installing">Installing</a>
- <a href="install/#optional-requirements">Optional requirements</a>
-
- <a href="basicuse/#basic-usage">Basic Usage</a>
- <a href="basicuse/#load-and-dump">Load and dump </a>
- <a href="basicuse/#more-examples">More examples</a>
-
- <a href="dumpcls/#working-with-python-classes">Working with Python classes</a>
- <a href="dumpcls/#dumping-python-classes">Dumping Python classes</a>
- <a href="dumpcls/#dataclass">Dataclass</a>
-
- <a href="detail/#details">Details</a>
- <a href="detail/#indentation-of-block-sequences">Indentation of block sequences</a>
- <a href="detail/#inconsistently-indented-yaml">Inconsistently indented YAML</a>
- <a href="detail/#indenting-using-typsafe">Indenting using `typ="safe"`</a>
- <a href="detail/#positioning-in-top-level-mappings-prefixing">Positioning ':' in top level mappings, prefixing ':'</a>
- <a href="detail/#document-version-support">Document version support</a>
- <a href="detail/#round-trip-including-comments">Round trip including comments</a>
- <a href="detail/#config-file-formats">Config file formats</a>
- <a href="detail/#extending">Extending</a>
- <a href="detail/#smartening">Smartening</a>
-
- <a href="example/#examples">Examples</a>
- <a href="example/#output-of-dump-as-a-string">Output of `dump()` as a string</a>
-
- <a href="api/#departure-from-previous-api">Departure from previous API</a>
- <a href="api/#loading">Loading</a>
- <a href="api/#duplicate-keys">Duplicate keys</a>
- <a href="api/#dumping-a-multi-document-yaml-stream">Dumping a multi-document YAML stream</a>
- <a href="api/#dumping">Dumping</a>
- <a href="api/#controls">Controls</a>
- <a href="api/#transparent-usage-of-new-and-old-api">Transparent usage of new and old API</a>
- <a href="api/#reason-for-api-change">Reason for API change</a>
-
- <a href="pyyaml/#differences-with-pyyaml">Differences with PyYAML</a>
- <a href="pyyaml/#defaulting-to-yaml-12-support">Defaulting to YAML 1.2 support</a>
- <a href="pyyaml/#py2py3-reintegration">PY2/PY3 reintegration</a>
- <a href="pyyaml/#fixes">Fixes</a>
- <a href="pyyaml/#testing">Testing</a>
- <a href="pyyaml/#api">API</a>
-
- <a href="contributing/#contributing">Contributing</a>
- <a href="contributing/#documentation">Documentation</a>
- <a href="contributing/#code">Code</a>
- <a href="contributing/#flake">Flake</a>
- <a href="contributing/#toxpytest">Tox/pytest</a>
- <a href="contributing/#typingmypy">Typing/mypy</a>
- <a href="contributing/#generated-files">Generated files</a>
- <a href="contributing/#vulnerabilities">Vulnerabilities</a>
-</pre>
-
-
-[![image](https://readthedocs.org/projects/yaml/badge/?version=latest)](https://yaml.readthedocs.org/en/latest?badge=latest)[![image](https://bestpractices.coreinfrastructure.org/projects/1128/badge)](https://bestpractices.coreinfrastructure.org/projects/1128)
-[![image](https://sourceforge.net/p/ruamel-yaml/code/ci/default/tree/_doc/_static/license.svg?format=raw)](https://opensource.org/licenses/MIT)
-[![image](https://sourceforge.net/p/ruamel-yaml/code/ci/default/tree/_doc/_static/pypi.svg?format=raw)](https://pypi.org/project/ruamel.yaml/)
-[![image](https://sourceforge.net/p/oitnb/code/ci/default/tree/_doc/_static/oitnb.svg?format=raw)](https://pypi.org/project/oitnb/)
-[![image](http://www.mypy-lang.org/static/mypy_badge.svg)](http://mypy-lang.org/)
-
-# ChangeLog
-
-0.18.5 (2023-11-03):
-
-- there is some indication that dependent packages have been pinned to use specific (tested) and just install the latest even in Python versions that have end-of-life
-
-0.18.4 (2023-11-01):
-
-- YAML() instance has a `doc_infos` attribute which is a cumulative list of DocInfo instances (one for `load()`, one per document for `load_all()`). DocInfo instances contain version information (requested, directive) and tag directive information
-- fix issue that the YAML instance tags attribute was not reset between documents, resulting in mixing of tag directives of multiple documents. Now only provides tag directive information on latest document after loading. This means tags for dumping must be set **again** after a document is loaded with the same instance. (because of this tags will be removed in a favour of a different mechanism in the future)
-- fix issue with multiple document intermixing YAML 1.2 and YAML 1.1, the VersionedResolver now resets
-- fix issue with disappearing comment when next token was Tag (still can't have both a comment before a tag and after a tag, before node)
-
-0.18.3 (2023-10-29):
-
-- fix issue with spurious newline on first item after comment + nested block sequence
-- additional links in the metadata on PyPI (Reported, with pointers how to fix, by [Sorin](https://sourceforge.net/u/ssbarnea/profile/)).
-
-0.18.2 (2023-10-24):
-
-- calling the deprecated functions now raises an `AttributeError` with the, somewhat more informative, orginal warning message. Instead of calling `sys.exit(1)`
-
-0.18.1 (2023-10-24):
-
-- calling the deprecated functions now always displays the warning message. (reported by [Trend Lloyd](https://sourceforge.net/u/lathiat2/profile/))
-
-0.18.0 (2023-10-23):
-
-- the **functions** `scan`, `parse`, `compose`, `load`, `emit`, `serialize`, `dump` and their variants (`_all`, `safe_`, `round_trip_`, etc) have been deprecated (the same named **methods** on `YAML()` instances are, of course, still there.
-- `YAML(typ='unsafe')` now issues a `PendingDeprecationWarning`. This will become deprecated in the 0.18 series
-(probably before the end of 2023).
-You can use `YAML(typ='full')` to dump unregistered Python classes/functions.
-For loading you'll have to register your classes/functions
-if you want the old, unsafe, functionality. You can still load any tag, like `!!python/name:posix.system', **safely**
-with the (default) round-trip parser.
-- fix for `bytes-like object is required not 'str' while dumping binary streams`. This was reported, analysed and a fix provided by [Vit Zikmund](https://sourceforge.net/u/tlwhitec/profile/)
-
-0.17.40 (2023-10-20):
-
-- flow style sets are now preserved ( `!!set {a, b, c} )`. Any values specified when loading are dropped, including `!!null ""`.
-- potential workaround for issue 484: the long_description_content_type including the variant specification `CommonMark`
-can result in problems on Azure. If you can install from `.tar.gz` using
-`RUAMEL_NO_LONG_DESCRIPTION=1 pip install ruamel.yaml --no-binary :all:` then the long description, and its
-offending type, are nog included (in the METADATA).
-(Reported by [Coury Ditch](https://sourceforge.net/u/cmditch/profile/))
-- links in documentation update (reported by [David Hoese](https://sourceforge.net/u/daveydave400/profile/))
-- Added some `__repr__` for internally used classes
-
-0.17.39 (2023-10-19):
-
-- update README generation, no code changes
-
-0.17.36 (2023-10-19):
-
-- fixed issue 480, dumping of a loaded empty flow-style mapping with comment failed (Reported by [Stéphane Brunner](https://sourceforge.net/u/stbrunner/profile/))
-- fixed issue 482, caused by DEFAULT_MAPPING_TAG having changes to being a `Tag()` instance, not a string (reported by [yan12125](https://sourceforge.net/u/yan12125/profile/))
-- updated documentation to use mkdocs
-
-0.17.35 (2023-10-04):
-
-- support for loading dataclasses with `InitVar` variables (some special coding was necessary to get the, unexecpected, default value in the corresponding instance attribute ( example of usage in [this question](https://stackoverflow.com/q/77228378/1307905))
-
-0.17.34 (2023-10-03):
-
-- Python 3.12 also loads C version when using `typ='safe'`
-- initial support for loading invoking
-`__post_init__()` on dataclasses that have that
-method after loading a registered dataclass.
-(Originally
-[asked](https://stackoverflow.com/q/51529458/1307905) on
-Stackoverflow by
-[nyanpasu64](https://stackoverflow.com/users/2683842/nyanpasu64)
-and as
-[ticket](https://sourceforge.net/p/ruamel-yaml/tickets/355/) by
-[Patrick Lehmann](https://sourceforge.net/u/paebbels/profile/))
-
-```
-@yaml.register_class
-@dataclass
-class ...
-```
-
-0.17.33 (2023-09-28):
-
-- added `flow_seq_start`, `flow_seq_end`, `flow_seq_separator`, `flow_map_start`, `flow_map_end`, `flow_map_separator` **class** attributes to the `Emitter` class so flow style output can more easily be influenced (based on [this answer](https://stackoverflow.com/a/76547814/1307905) on a StackOverflow question by [Huw Walters](https://stackoverflow.com/users/291033/huw-walters)).
-
-0.17.32 (2023-06-17):
-
-- fix issue with scanner getting stuck in infinite loop
-
-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
-
-0.17.25 (2023-05-09):
-
-- fix for regression while dumping wrapped strings with too many backslashes removed (issue 459, reported by [Lele Gaifax](https://sourceforge.net/u/lele/profile/))
-
-0.17.24 (2023-05-06):
-
-- rewrite of `CommentedMap.insert()`. If you have a merge key in the YAML document for the mapping you insert to, the position value should be the one as you look at the YAML input. This fixes issue 453 where other keys of a merged in mapping would show up after an insert (reported by [Alex Miller](https://sourceforge.net/u/millerdevel/profile/)). It also fixes a call to `.insert()` resulting into the merge key to move to be the first key if it wasn't already and it is also now possible to insert a key before a merge key (even if the fist key in the mapping).
-- fix (in the pure Python implementation including default) for issue 447. (reported by [Jack Cherng](https://sourceforge.net/u/jfcherng/profile/), also brought up by brent on [StackOverflow](https://stackoverflow.com/q/40072485/1307905))
-
-0.17.23 (2023-05-05):
-
-- fix 458, error on plain scalars starting with word longer than width. (reported by [Kyle Larose](https://sourceforge.net/u/klarose/profile/))
-- fix for `.update()` no longer correctly handling keyword arguments (reported by John Lin on [StackOverflow]( https://stackoverflow.com/q/76089100/1307905))
-- fix issue 454: high Unicode (emojis) in quoted strings always
-escaped (reported by [Michal
-Čihař](https://sourceforge.net/u/nijel/profile/) based on a
-question on StackOverflow).
-- fix issue with emitter conservatively inserting extra backslashes in wrapped quoted strings (reported by thebenman on [StackOverflow](https://stackoverflow.com/q/75631454/1307905))
-
-0.17.22 (2023-05-02):
-
-- fix issue 449 where the second exclamation marks got URL encoded (reported and fixing PR provided by [John Stark](https://sourceforge.net/u/jods/profile/))
-- fix issue with indent != 2 and literal scalars with empty first line (reported by wrdis on [StackOverflow](https://stackoverflow.com/q/75584262/1307905))
-- updated `__repr__` of CommentedMap, now that Python's dict is ordered -> no more `ordereddict(list-of-tuples)`
-- merge MR 4, handling OctalInt in YAML 1.1 (provided by [Jacob Floyd](https://sourceforge.net/u/cognifloyd/profile/))
-- fix loading of `!!float 42` (reported by Eric on [Stack overflow](https://stackoverflow.com/a/71555107/1307905))
-- line numbers are now set on `CommentedKeySeq` and `CommentedKeyMap` (which are created if you have a sequence resp. mapping as the key in a mapping)
-- plain scalars: put single words longer than width on a line of
-their own, instead of after the previous line (issue 427, reported
-by [Antoine
-Cotten](https://sourceforge.net/u/antoineco/profile/)). Caveat:
-this currently results in a space ending the previous line.
-- fix for folded scalar part of 421: comments after ">" on first
-line of folded scalars are now preserved (as were those in the
-same position on literal scalars). Issue reported by Jacob Floyd.
-- added stacklevel to warnings
-- typing changed from Py2 compatible comments to Py3, removed various Py2-isms
-
-0.17.21 (2022-02-12):
-
-- fix bug in calling `.compose()` method with `pathlib.Path` instance.
-
-0.17.20 (2022-01-03):
-
-- fix error in microseconds while rounding datetime fractions >= 9999995 (reported by [Luis Ferreira](https://sourceforge.net/u/ljmf00/))
-
-0.17.19 (2021-12-26):
-
-- fix mypy problems (reported by [Arun](https://sourceforge.net/u/arunppsg/profile/))
-
-0.17.18 (2021-12-24):
-
-- copy-paste error in folded scalar comment attachment (reported by [Stephan Geulette](https://sourceforge.net/u/sgeulette/profile/))
-- fix 411, indent error comment between key empty seq value (reported by [Guillermo Julián](https://sourceforge.net/u/gjulianm/profile/))
-
-0.17.17 (2021-10-31):
-
-- extract timestamp matching/creation to util
-
-0.17.16 (2021-08-28):
-
-- 398 also handle issue 397 when comment is newline
-
-0.17.15 (2021-08-28):
-
-- fix issue 397, insert comment before key when a comment between key and value exists (reported by [Bastien gerard](https://sourceforge.net/u/bagerard/))
-
-0.17.14 (2021-08-25):
-
-- fix issue 396, inserting key/val in merged-in dictionary (reported by [Bastien gerard](https://sourceforge.net/u/bagerard/))
-
-0.17.13 (2021-08-21):
-
-- minor fix in attr handling
-
-0.17.12 (2021-08-21):
-
-- fix issue with anchor on registered class not preserved and those classes using package attrs with `@attr.s()` (both reported by [ssph](https://sourceforge.net/u/sph/))
-
-0.17.11 (2021-08-19):
-
-- 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/))
-
-0.17.10 (2021-06-24):
-
-- fix issue 388, token with old comment structure != two elements (reported by [Dimitrios Bariamis](https://sourceforge.net/u/dbdbc/))
-
-0.17.9 (2021-06-10):
-
-- fix issue with updating CommentedMap (reported by sri on [StackOverflow](https://stackoverflow.com/q/67911659/1307905))
-
-0.17.8 (2021-06-09):
-
-- fix for issue 387 where templated anchors on tagged object did get set resulting in potential id reuse. (reported by [Artem Ploujnikov](https://sourceforge.net/u/flexthink/))
-
-0.17.7 (2021-05-31):
-
-- issue 385 also affected other deprecated loaders (reported via email by Oren Watson)
-
-0.17.6 (2021-05-31):
-
-- merged type annotations update provided by [Jochen Sprickerhof](https://sourceforge.net/u/jspricke/)
-- fix for issue 385: deprecated round_trip_loader function not
-working (reported by [Mike
-Gouline](https://sourceforge.net/u/gouline/))
-- wasted a few hours getting rid of mypy warnings/errors
-
-0.17.5 (2021-05-30):
-
-- fix for issue 384 `!!set` with aliased entry resulting in broken YAML on rt reported by [William Kimball](https://sourceforge.net/u/william303/))
-
-0.17.4 (2021-04-07):
-
-- prevent (empty) comments from throwing assertion error (issue 351 reported by [William Kimball](https://sourceforge.net/u/william303/)) comments (or empty line) will be dropped
-
-0.17.3 (2021-04-07):
-
-- fix for issue 382 caused by an error in a format string (reported by [William Kimball](https://sourceforge.net/u/william303/))
-- allow expansion of aliases by setting `yaml.composer.return_alias = lambda s: copy.deepcopy(s)`
-(as per [Stackoverflow answer](https://stackoverflow.com/a/66983530/1307905))
-
-0.17.2 (2021-03-29):
-
-- change -py2.py3-none-any.whl to -py3-none-any.whl, and remove 0.17.1
-
-0.17.1 (2021-03-29):
-
-- added 'Programming Language :: Python :: 3 :: Only', and
-removing 0.17.0 from PyPI (reported by [Alasdair
-Nicol](https://sourceforge.net/u/alasdairnicol/))
-
-0.17.0 (2021-03-26):
-
-- removed because of incomplete classifiers
-- this release no longer supports Python 2.7, most if not all Python 2 specific code is removed. The 0.17.x series is the last to support Python 3.5 (this also allowed for removal of the dependency on `ruamel.std.pathlib`)
-- remove Python2 specific code branches and adaptations (u-strings)
-- prepare % code for f-strings using `_F`
-- allow PyOxidisation ([issue 324](https://sourceforge.net/p/ruamel-yaml/tickets/324/) resp. [issue 171](https://github.com/indygreg/PyOxidizer/issues/171))
-- replaced Python 2 compatible enforcement of keyword arguments with '*'
-- the old top level *functions* `load`, `safe_load`, `round_trip_load`, `dump`, `safe_dump`, `round_trip_dump`, `scan`, `parse`, `compose`, `emit`, `serialize` as well as their `_all` variants for multi-document streams, now issue a `PendingDeprecationning` (e.g. when run from pytest, but also Python is started with `-Wd`). Use the methods on `YAML()`, which have been extended.
-- fix for issue 376: indentation changes could put literal/folded
-scalar to start before the `#` column of a following comment.
-Effectively making the comment part of the scalar in the output.
-(reported by [Bence Nagy](https://sourceforge.net/u/underyx/))
-
-------------------------------------------------------------------------
-
-For older changes see the file
-[CHANGES](https://sourceforge.net/p/ruamel-yaml/code/ci/default/tree/CHANGES)
diff --git a/README.md b/README.md
index bf3d8cc..8e24a45 100644
--- a/README.md
+++ b/README.md
@@ -1,12 +1,13 @@
+
# ruamel.yaml
`ruamel.yaml` is a YAML 1.2 loader/dumper package for Python.
<table class="docutils">
<tr> <td>version</td>
- <td>0.18.5</td>
+ <td>0.18.6</td>
</tr>
<tr> <td>updated</td>
- <td>2023-11-03</td>
+ <td>2024-02-07</td>
</tr>
<tr> <td>documentation</td>
<td><a href="https://yaml.readthedocs.io">https://yaml.readthedocs.io</a></td>
@@ -19,6 +20,7 @@
</tr>
</table>
+
As announced, in 0.18.0, the old PyYAML functions have been deprecated.
(`scan`, `parse`, `compose`, `load`, `emit`, `serialize`, `dump` and their variants
(`_all`, `safe_`, `round_trip_`, etc)). If you only read this after your program has
@@ -120,6 +122,7 @@ the API is stable enough to make the transition.
</pre>
+
[![image](https://readthedocs.org/projects/yaml/badge/?version=latest)](https://yaml.readthedocs.org/en/latest?badge=latest)[![image](https://bestpractices.coreinfrastructure.org/projects/1128/badge)](https://bestpractices.coreinfrastructure.org/projects/1128)
[![image](https://sourceforge.net/p/ruamel-yaml/code/ci/default/tree/_doc/_static/license.svg?format=raw)](https://opensource.org/licenses/MIT)
[![image](https://sourceforge.net/p/ruamel-yaml/code/ci/default/tree/_doc/_static/pypi.svg?format=raw)](https://pypi.org/project/ruamel.yaml/)
@@ -128,6 +131,11 @@ the API is stable enough to make the transition.
# ChangeLog
+0.18.6 (2024-02-07):
+
+- fixed an issue with dataclass loading when the fields were collections (bug found as a result of a question by [FibroMyAlgebra](https://stackoverflow.com/users/6855070/fibromyalgebra) on [StackOverflow](https://stackoverflow.com/a/77485786/1307905))
+- fixed an issue loading dataclasses with `InitVar` fields when `from __future__ import annotations` was used to delay evaluation of typing.
+
0.18.5 (2023-11-03):
- there is some indication that dependent packages have been pinned to use specific (tested) and just install the latest even in Python versions that have end-of-life
@@ -390,6 +398,7 @@ scalar to start before the `#` column of a following comment.
Effectively making the comment part of the scalar in the output.
(reported by [Bence Nagy](https://sourceforge.net/u/underyx/))
+
------------------------------------------------------------------------
For older changes see the file
diff --git a/__init__.py b/__init__.py
index f8cfa43..cf9a01d 100644
--- a/__init__.py
+++ b/__init__.py
@@ -1,13 +1,14 @@
-# coding: utf-8
+
+from __future__ import annotations
if False: # MYPY
from typing import Dict, Any # NOQA
_package_data = dict(
full_package_name='ruamel.yaml',
- version_info=(0, 18, 5),
- __version__='0.18.5',
- version_timestamp='2023-11-03 08:54:26',
+ version_info=(0, 18, 6),
+ __version__='0.18.6',
+ version_timestamp='2024-02-07 07:43:33',
author='Anthon van der Neut',
author_email='a.van.der.neut@ruamel.eu',
description='ruamel.yaml is a YAML parser/emitter that supports roundtrip preservation of comments, seq/map flow style, and map key order', # NOQA
diff --git a/_doc/Makefile b/_doc/Makefile
new file mode 100644
index 0000000..c5d1aa0
--- /dev/null
+++ b/_doc/Makefile
@@ -0,0 +1,216 @@
+# Makefile for Sphinx documentation
+#
+
+# You can set these variables from the command line.
+SPHINXOPTS =
+SPHINXBUILD = sphinx-build
+PAPER = a4
+BUILDDIR = _build
+
+# User-friendly check for sphinx-build
+ifeq ($(shell which $(SPHINXBUILD) >/dev/null 2>&1; echo $$?), 1)
+$(error The '$(SPHINXBUILD)' command was not found. Make sure you have Sphinx installed, then set the SPHINXBUILD environment variable to point to the full path of the '$(SPHINXBUILD)' executable. Alternatively you can add the directory with the executable to your PATH. If you don't have Sphinx installed, grab it from http://sphinx-doc.org/)
+endif
+
+# Internal variables.
+PAPEROPT_a4 = -D latex_paper_size=a4
+PAPEROPT_letter = -D latex_paper_size=letter
+ALLSPHINXOPTS = -d $(BUILDDIR)/doctrees $(PAPEROPT_$(PAPER)) $(SPHINXOPTS) .
+# the i18n builder cannot share the environment and doctrees with the others
+I18NSPHINXOPTS = $(PAPEROPT_$(PAPER)) $(SPHINXOPTS) .
+
+.PHONY: help
+help:
+ @echo "Please use \`make <target>' where <target> is one of"
+ @echo " html to make standalone HTML files"
+ @echo " dirhtml to make HTML files named index.html in directories"
+ @echo " singlehtml to make a single large HTML file"
+ @echo " pickle to make pickle files"
+ @echo " json to make JSON files"
+ @echo " htmlhelp to make HTML files and a HTML help project"
+ @echo " qthelp to make HTML files and a qthelp project"
+ @echo " applehelp to make an Apple Help Book"
+ @echo " devhelp to make HTML files and a Devhelp project"
+ @echo " epub to make an epub"
+ @echo " latex to make LaTeX files, you can set PAPER=a4 or PAPER=letter"
+ @echo " latexpdf to make LaTeX files and run them through pdflatex"
+ @echo " latexpdfja to make LaTeX files and run them through platex/dvipdfmx"
+ @echo " text to make text files"
+ @echo " man to make manual pages"
+ @echo " texinfo to make Texinfo files"
+ @echo " info to make Texinfo files and run them through makeinfo"
+ @echo " gettext to make PO message catalogs"
+ @echo " changes to make an overview of all changed/added/deprecated items"
+ @echo " xml to make Docutils-native XML files"
+ @echo " pseudoxml to make pseudoxml-XML files for display purposes"
+ @echo " linkcheck to check all external links for integrity"
+ @echo " doctest to run all doctests embedded in the documentation (if enabled)"
+ @echo " coverage to run coverage check of the documentation (if enabled)"
+
+.PHONY: clean
+clean:
+ rm -rf $(BUILDDIR)/*
+
+.PHONY: html
+html:
+ $(SPHINXBUILD) -b html $(ALLSPHINXOPTS) $(BUILDDIR)/html
+ @echo
+ @echo "Build finished. The HTML pages are in $(BUILDDIR)/html."
+
+.PHONY: dirhtml
+dirhtml:
+ $(SPHINXBUILD) -b dirhtml $(ALLSPHINXOPTS) $(BUILDDIR)/dirhtml
+ @echo
+ @echo "Build finished. The HTML pages are in $(BUILDDIR)/dirhtml."
+
+.PHONY: singlehtml
+singlehtml:
+ $(SPHINXBUILD) -b singlehtml $(ALLSPHINXOPTS) $(BUILDDIR)/singlehtml
+ @echo
+ @echo "Build finished. The HTML page is in $(BUILDDIR)/singlehtml."
+
+.PHONY: pickle
+pickle:
+ $(SPHINXBUILD) -b pickle $(ALLSPHINXOPTS) $(BUILDDIR)/pickle
+ @echo
+ @echo "Build finished; now you can process the pickle files."
+
+.PHONY: json
+json:
+ $(SPHINXBUILD) -b json $(ALLSPHINXOPTS) $(BUILDDIR)/json
+ @echo
+ @echo "Build finished; now you can process the JSON files."
+
+.PHONY: htmlhelp
+htmlhelp:
+ $(SPHINXBUILD) -b htmlhelp $(ALLSPHINXOPTS) $(BUILDDIR)/htmlhelp
+ @echo
+ @echo "Build finished; now you can run HTML Help Workshop with the" \
+ ".hhp project file in $(BUILDDIR)/htmlhelp."
+
+.PHONY: qthelp
+qthelp:
+ $(SPHINXBUILD) -b qthelp $(ALLSPHINXOPTS) $(BUILDDIR)/qthelp
+ @echo
+ @echo "Build finished; now you can run "qcollectiongenerator" with the" \
+ ".qhcp project file in $(BUILDDIR)/qthelp, like this:"
+ @echo "# qcollectiongenerator $(BUILDDIR)/qthelp/yaml.qhcp"
+ @echo "To view the help file:"
+ @echo "# assistant -collectionFile $(BUILDDIR)/qthelp/yaml.qhc"
+
+.PHONY: applehelp
+applehelp:
+ $(SPHINXBUILD) -b applehelp $(ALLSPHINXOPTS) $(BUILDDIR)/applehelp
+ @echo
+ @echo "Build finished. The help book is in $(BUILDDIR)/applehelp."
+ @echo "N.B. You won't be able to view it unless you put it in" \
+ "~/Library/Documentation/Help or install it in your application" \
+ "bundle."
+
+.PHONY: devhelp
+devhelp:
+ $(SPHINXBUILD) -b devhelp $(ALLSPHINXOPTS) $(BUILDDIR)/devhelp
+ @echo
+ @echo "Build finished."
+ @echo "To view the help file:"
+ @echo "# mkdir -p $$HOME/.local/share/devhelp/yaml"
+ @echo "# ln -s $(BUILDDIR)/devhelp $$HOME/.local/share/devhelp/yaml"
+ @echo "# devhelp"
+
+.PHONY: epub
+epub:
+ $(SPHINXBUILD) -b epub $(ALLSPHINXOPTS) $(BUILDDIR)/epub
+ @echo
+ @echo "Build finished. The epub file is in $(BUILDDIR)/epub."
+
+.PHONY: latex
+latex:
+ $(SPHINXBUILD) -b latex $(ALLSPHINXOPTS) $(BUILDDIR)/latex
+ @echo
+ @echo "Build finished; the LaTeX files are in $(BUILDDIR)/latex."
+ @echo "Run \`make' in that directory to run these through (pdf)latex" \
+ "(use \`make latexpdf' here to do that automatically)."
+
+.PHONY: latexpdf
+latexpdf:
+ $(SPHINXBUILD) -b latex $(ALLSPHINXOPTS) $(BUILDDIR)/latex
+ @echo "Running LaTeX files through pdflatex..."
+ $(MAKE) -C $(BUILDDIR)/latex all-pdf
+ @echo "pdflatex finished; the PDF files are in $(BUILDDIR)/latex."
+
+.PHONY: latexpdfja
+latexpdfja:
+ $(SPHINXBUILD) -b latex $(ALLSPHINXOPTS) $(BUILDDIR)/latex
+ @echo "Running LaTeX files through platex and dvipdfmx..."
+ $(MAKE) -C $(BUILDDIR)/latex all-pdf-ja
+ @echo "pdflatex finished; the PDF files are in $(BUILDDIR)/latex."
+
+.PHONY: text
+text:
+ $(SPHINXBUILD) -b text $(ALLSPHINXOPTS) $(BUILDDIR)/text
+ @echo
+ @echo "Build finished. The text files are in $(BUILDDIR)/text."
+
+.PHONY: man
+man:
+ $(SPHINXBUILD) -b man $(ALLSPHINXOPTS) $(BUILDDIR)/man
+ @echo
+ @echo "Build finished. The manual pages are in $(BUILDDIR)/man."
+
+.PHONY: texinfo
+texinfo:
+ $(SPHINXBUILD) -b texinfo $(ALLSPHINXOPTS) $(BUILDDIR)/texinfo
+ @echo
+ @echo "Build finished. The Texinfo files are in $(BUILDDIR)/texinfo."
+ @echo "Run \`make' in that directory to run these through makeinfo" \
+ "(use \`make info' here to do that automatically)."
+
+.PHONY: info
+info:
+ $(SPHINXBUILD) -b texinfo $(ALLSPHINXOPTS) $(BUILDDIR)/texinfo
+ @echo "Running Texinfo files through makeinfo..."
+ make -C $(BUILDDIR)/texinfo info
+ @echo "makeinfo finished; the Info files are in $(BUILDDIR)/texinfo."
+
+.PHONY: gettext
+gettext:
+ $(SPHINXBUILD) -b gettext $(I18NSPHINXOPTS) $(BUILDDIR)/locale
+ @echo
+ @echo "Build finished. The message catalogs are in $(BUILDDIR)/locale."
+
+.PHONY: changes
+changes:
+ $(SPHINXBUILD) -b changes $(ALLSPHINXOPTS) $(BUILDDIR)/changes
+ @echo
+ @echo "The overview file is in $(BUILDDIR)/changes."
+
+.PHONY: linkcheck
+linkcheck:
+ $(SPHINXBUILD) -b linkcheck $(ALLSPHINXOPTS) $(BUILDDIR)/linkcheck
+ @echo
+ @echo "Link check complete; look for any errors in the above output " \
+ "or in $(BUILDDIR)/linkcheck/output.txt."
+
+.PHONY: doctest
+doctest:
+ $(SPHINXBUILD) -b doctest $(ALLSPHINXOPTS) $(BUILDDIR)/doctest
+ @echo "Testing of doctests in the sources finished, look at the " \
+ "results in $(BUILDDIR)/doctest/output.txt."
+
+.PHONY: coverage
+coverage:
+ $(SPHINXBUILD) -b coverage $(ALLSPHINXOPTS) $(BUILDDIR)/coverage
+ @echo "Testing of coverage in the sources finished, look at the " \
+ "results in $(BUILDDIR)/coverage/python.txt."
+
+.PHONY: xml
+xml:
+ $(SPHINXBUILD) -b xml $(ALLSPHINXOPTS) $(BUILDDIR)/xml
+ @echo
+ @echo "Build finished. The XML files are in $(BUILDDIR)/xml."
+
+.PHONY: pseudoxml
+pseudoxml:
+ $(SPHINXBUILD) -b pseudoxml $(ALLSPHINXOPTS) $(BUILDDIR)/pseudoxml
+ @echo
+ @echo "Build finished. The pseudo-XML files are in $(BUILDDIR)/pseudoxml."
diff --git a/_doc/README.ryd b/_doc/README.ryd
new file mode 100644
index 0000000..f728607
--- /dev/null
+++ b/_doc/README.ryd
@@ -0,0 +1,120 @@
+version: 0.2
+text: md
+pdf: false
+order:
+- overview.ryd
+- install.ryd
+- basicuse.ryd
+- dumpcls.ryd
+- detail.ryd
+- example.ryd
+- api.ryd
+- pyyaml.ryd
+- contributing.ryd
+toc: False # don't index this file or put in mkdocs.nav
+mkdocs:
+ site_name: yaml
+ docs_dir: _doc
+ site_author: Anthon van der Neut
+
+ nav:
+ - overview.md
+ - install.md
+ - basicuse.md
+ - dumpcls.md
+ - detail.md
+ - example.md
+ - api.md
+ - pyyaml.md
+ - contributing.md
+
+ theme:
+ name: readthedocs
+
+ exclude_docs: |
+ *.ryd
+ *.rst
+
+ markdown_extensions:
+ - toc:
+ permalink: true
+--- |
+# ruamel.yaml
+
+`ruamel.yaml` is a YAML 1.2 loader/dumper package for Python.
+
+--- !table
+version: !Env version
+updated: !Env date
+documentation: https://yaml.readthedocs.io
+repository: https://sourceforge.net/projects/ruamel-yaml
+pypi: https://pypi.org/project/ruamel.yaml
+--- |
+
+As announced, in 0.18.0, the old PyYAML functions have been deprecated.
+(`scan`, `parse`, `compose`, `load`, `emit`, `serialize`, `dump` and their variants
+(`_all`, `safe_`, `round_trip_`, etc)). If you only read this after your program has
+stopped working: I am sorry to hear that, but that also means you, or the person
+developing your program, has not tested with warnings on (which is the recommendation
+in PEP 565, and e.g. defaultin when using `pytest`). If you have troubles, explicitly use
+```
+pip install "ruamel.yaml<0.18.0"
+```
+or put something to that effects in your requirments, to give yourself
+some time to solve the issue.
+
+There will be at least one more potentially breaking change in the 0.18 series: `YAML(typ='unsafe')`
+now has a pending deprecation warning and is going to be deprecated, probably before the end of 2023.
+If you only use it to dump, please use the new `YAML(typ='full')`, the result of that can be *safely*
+loaded with a default instance `YAML()`, as that will get you inspectable, tagged, scalars, instead of
+executed Python functions/classes. (You should probably add constructors for what you actually need,
+but I do consider adding a `ruamel.yaml.unsafe` package that will re-add the `typ='unsafe'` option.
+*Please adjust/pin your dependencies accordingly if necessary.*
+
+
+There seems to be a CVE on `ruamel.yaml`, stating that the `load()` function could be abused
+because of unchecked input. `load()` was never the default function (that was `round_trip_load()`
+before the new API came into existence`. So the creator of that CVE was ill informed and
+probably lazily assumed that since `ruamel.yaml` is a derivative of PyYAML (for which
+a similar CVE exists), the same problem would still exist, without checking.
+So the CVE was always inappriate, now just more so, as the call
+to the function `load()` with any input will terminate your program with an error message. If you
+(have to) care about such things as this CVE, my recommendation is to stop using Python
+completely, as `pickle.load()` can be abused in the same way as `load()` (and like unlike `load()`
+is only documented to be unsafe, without development-time warning.
+
+Version 0.17.21 was the last one tested to be working on Python 3.5 and 3.6<BR>
+The 0.16.13 release was the last that was tested to be working on Python 2.7.
+
+
+There are two extra plug-in packages
+(`ruamel.yaml.bytes` and `ruamel.yaml.string`)
+for those not wanting to do the streaming to a
+`io.BytesIO/StringIO` buffer themselves.
+
+If your package uses `ruamel.yaml` and is not listed on PyPI, drop me an
+email, preferably with some information on how you use the package (or a
+link to the repository) and I'll keep you informed when the status of
+the API is stable enough to make the transition.
+
+--- !toc
+level: 3
+# prefix: http://yaml.readthedocs.io/en/latest/
+--- |
+
+[![image](https://readthedocs.org/projects/yaml/badge/?version=latest)](https://yaml.readthedocs.org/en/latest?badge=latest)[![image](https://bestpractices.coreinfrastructure.org/projects/1128/badge)](https://bestpractices.coreinfrastructure.org/projects/1128)
+[![image](https://sourceforge.net/p/ruamel-yaml/code/ci/default/tree/_doc/_static/license.svg?format=raw)](https://opensource.org/licenses/MIT)
+[![image](https://sourceforge.net/p/ruamel-yaml/code/ci/default/tree/_doc/_static/pypi.svg?format=raw)](https://pypi.org/project/ruamel.yaml/)
+[![image](https://sourceforge.net/p/oitnb/code/ci/default/tree/_doc/_static/oitnb.svg?format=raw)](https://pypi.org/project/oitnb/)
+[![image](http://www.mypy-lang.org/static/mypy_badge.svg)](http://mypy-lang.org/)
+
+# ChangeLog
+
+--- !changelog
+CHANGES
+--- |
+
+------------------------------------------------------------------------
+
+For older changes see the file
+[CHANGES](https://sourceforge.net/p/ruamel-yaml/code/ci/default/tree/CHANGES)
diff --git a/_doc/_static/license.svg b/_doc/_static/license.svg
new file mode 100644
index 0000000..43dbd86
--- /dev/null
+++ b/_doc/_static/license.svg
@@ -0,0 +1 @@
+<svg xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" width="82" height="20"><linearGradient id="b" x2="0" y2="100%"><stop offset="0" stop-color="#bbb" stop-opacity=".1"/><stop offset="1" stop-opacity=".1"/></linearGradient><clipPath id="a"><rect width="82" height="20" rx="3" fill="#fff"/></clipPath><g clip-path="url(#a)"><path fill="#555" d="M0 0h51v20H0z"/><path fill="#007ec6" d="M51 0h31v20H51z"/><path fill="url(#b)" d="M0 0h82v20H0z"/></g><g fill="#fff" text-anchor="middle" font-family="DejaVu Sans,Verdana,Geneva,sans-serif" font-size="110"><text x="265" y="150" fill="#010101" fill-opacity=".3" transform="scale(.1)" textLength="410">License</text><text x="265" y="140" transform="scale(.1)" textLength="410">License</text><text x="655" y="150" fill="#010101" fill-opacity=".3" transform="scale(.1)" textLength="210">MIT</text><text x="655" y="140" transform="scale(.1)" textLength="210">MIT</text></g> </svg>
diff --git a/_doc/_static/pypi.svg b/_doc/_static/pypi.svg
new file mode 100644
index 0000000..28c535d
--- /dev/null
+++ b/_doc/_static/pypi.svg
@@ -0,0 +1 @@
+<svg xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" width="86" height="20"><linearGradient id="b" x2="0" y2="100%"><stop offset="0" stop-color="#bbb" stop-opacity=".1"/><stop offset="1" stop-opacity=".1"/></linearGradient><clipPath id="a"><rect width="86" height="20" rx="3" fill="#fff"/></clipPath><g clip-path="url(#a)"><path fill="#555" d="M0 0h33v20H0z"/><path fill="#007ec6" d="M33 0h53v20H33z"/><path fill="url(#b)" d="M0 0h86v20H0z"/></g><g fill="#fff" text-anchor="middle" font-family="DejaVu Sans,Verdana,Geneva,sans-serif" font-size="110"> <text x="175" y="150" fill="#010101" fill-opacity=".3" transform="scale(.1)" textLength="230">pypi</text><text x="175" y="140" transform="scale(.1)" textLength="230">pypi</text><text x="585" y="150" fill="#010101" fill-opacity=".3" transform="scale(.1)" textLength="430">0.18.6</text><text x="585" y="140" transform="scale(.1)" textLength="430">0.18.6</text></g> </svg>
diff --git a/_doc/api.ryd b/_doc/api.ryd
new file mode 100644
index 0000000..c0e1a7d
--- /dev/null
+++ b/_doc/api.ryd
@@ -0,0 +1,308 @@
+version: 0.2
+text: md
+pdf: false
+--- !python-pre |
+import sys
+from io import StringIO
+import ruamel.yaml
+from ruamel.yaml import YAML
+yaml=YAML()
+ostream = s = StringIO()
+istream = stream = doc = "a: 1"
+data = dict(a=1)
+from pathlib import Path # or: from ruamel.std.pathlib import Path
+--- |
+# Departure from previous API
+
+With version 0.15.0 `ruamel.yaml` starts to depart from the previous
+(PyYAML) way of loading and dumping. During a transition period the
+original `load()` and `dump()` in its various formats will still be
+supported, but this is not guaranteed to be so with the transition to
+1.0.
+
+At the latest with 1.0, but possible earlier transition error and
+warning messages will be issued, so any packages depending on
+ruamel.yaml should pin the version with which they are testing.
+
+Up to 0.15.0, the loaders (`load()`, `safe_load()`, `round_trip_load()`,
+`load_all`, etc.) took, apart from the input stream, a `version`
+argument to allow downgrading to YAML 1.1, sometimes needed for
+documents without directive. When round-tripping, there was an option to
+preserve quotes.
+
+Up to 0.15.0, the dumpers (`dump()`, `safe_dump`, `round_trip_dump()`,
+`dump_all()`, etc.) had a plethora of arguments, some inherited from
+`PyYAML`, some added in `ruamel.yaml`. The only required argument is the
+`data` to be dumped. If the stream argument is not provided to the
+dumper, then a string representation is build up in memory and returned
+to the caller.
+
+Starting with 0.15.0 `load()` and `dump()` are methods on a `YAML`
+instance and only take the stream, resp. the data and stream argument.
+All other parameters are set on the instance of `YAML` before calling
+`load()` or `dump()`
+
+Before 0.15.0 you could do:
+
+``` python
+from pathlib import Path
+from ruamel import yaml
+
+data = yaml.safe_load("abc: 1")
+out = Path('/tmp/out.yaml')
+with out.open('w') as fp:
+ yaml.safe_dump(data, fp, default_flow_style=False)
+```
+
+after:
+--- !python |
+from pathlib import Path
+from ruamel.yaml import YAML
+
+yaml = YAML(typ='safe')
+yaml.default_flow_style = False
+data = yaml.load("abc: 1")
+out = Path('/tmp/out.yaml')
+yaml.dump(data, out)
+--- |
+If you previously used a keyword argument `explicit_start=True` you now
+do `yaml.explicit_start = True` before calling `dump()`. The `Loader`
+and `Dumper` keyword arguments are not supported that way. You can
+provide the `typ` keyword to `rt` (default), `safe`, `unsafe` or `base`
+(for round-trip load/dump, safe_load/dump, load/dump resp. using the
+BaseLoader / BaseDumper. More fine-control is possible by setting the
+attributes `.Parser`, `.Constructor`, `.Emitter`, etc., to the class of
+the type to create for that stage (typically a subclass of an existing
+class implementing that).
+
+The default loader (`typ='rt'`) is a direct derivative of the safe
+loader, without the methods to construct arbitrary Python objects that
+make the `unsafe` loader unsafe, but with the changes needed for
+round-trip preservation of comments, etc.. For trusted Python classes a
+constructor can of course be added to the round-trip or safe-loader, but
+this has to be done explicitly (`add_constructor`).
+
+All data is dumped (not just for round-trip-mode) with
+`.allow_unicode = True`
+
+You can of course have multiple YAML instances active at the same time,
+with different load and/or dump behaviour.
+
+Initially only the typical operations are supported, but in principle
+all functionality of the old interface will be available via `YAML`
+instances (if you are using something that isn\'t let me know).
+
+If a parse or dump fails, and throws and exception, the state of the
+`YAML()` instance is not guaranteed to be able to handle further
+processing. You should, at that point to recreate the YAML instance
+before proceeding.
+
+## Loading
+
+### Duplicate keys
+
+In JSON mapping keys should be unique, in YAML they must be unique.
+PyYAML never enforced this although the YAML 1.1 specification already
+required this.
+
+In the new API (starting 0.15.1) duplicate keys in mappings are no
+longer allowed by default. To allow duplicate keys in mappings:
+--- !python |
+yaml = ruamel.yaml.YAML()
+yaml.allow_duplicate_keys = True
+yaml.load(stream)
+--- |
+In the old API this is a warning starting with 0.15.2 and an error in
+0.16.0.
+
+When a duplicate key is found it and its value are discarded, as should
+be done according to the [YAML 1.1
+specification](http://yaml.org/spec/1.1/#id932806).
+
+## Dumping a multi-document YAML stream
+
+The \"normal\" `dump_all` expected as first element a list of documents,
+or something else the internals of the method can iterate over. To read
+and write a multi-document you would either make a `list`:
+--- !code |
+ yaml = YAML()
+ data = list(yaml.load_all(in_path))
+ # do something on data[0], data[1], etc.
+ yaml.dump_all(data, out_path)
+--- |
+or create some function/object that would yield the `data` values.
+
+What you now can do is create `YAML()` as an context manager. This works
+for output (dumping) only, requires you to specify the output (file,
+buffer, `Path`) at creation time, and doesn\'t support `transform`
+(yet).
+
+:
+--- !code |
+ with YAML(output=sys.stdout) as yaml:
+ yaml.explicit_start = True
+ for data in yaml.load_all(Path(multi_document_filename)):
+ # do something on data
+ yaml.dump(data)
+--- |
+Within the context manager, you cannot use the `dump()` with a second
+(stream) argument, nor can you use `dump_all()`. The `dump()` within the
+context of the `YAML()` automatically creates multi-document if called
+more than once.
+
+To combine multiple YAML documents from multiple files:
+
+:
+--- !code |
+ list_of_filenames = ['x.yaml', 'y.yaml', ]
+ with YAML(output=sys.stdout) as yaml:
+ yaml.explicit_start = True
+ for path in list_of_filename:
+ with open(path) as fp:
+ yaml.dump(yaml.load(fp))
+--- |
+The output will be a valid, uniformly indented YAML file. Doing
+`cat {x,y}.yaml` might result in a single document if there is not
+document start marker at the beginning of `y.yaml`
+
+## Dumping
+
+### Controls
+
+On your `YAML()` instance you can set attributes e.g with:
+
+ yaml = YAML(typ='safe', pure=True)
+ yaml.allow_unicode = False
+
+available attributes include:
+
+`unicode_supplementary`
+
+: Defaults to `True` if Python\'s Unicode size is larger than 2 bytes.
+ Set to `False` to enforce output of the form `\U0001f601` (ignored
+ if `allow_unicode` is `False`)
+
+## Transparent usage of new and old API
+
+With 0.18 the entry functions for the old API has been removed, so the
+following now only makes sense if you use the old API on a pinned
+old version or `ruamel.yaml`.
+
+If you have multiple packages depending on `ruamel.yaml`, or install
+your utility together with other packages not under your control, then
+fixing your `install_requires` might not be so easy.
+
+Depending on your usage you might be able to \"version\" your usage to
+be compatible with both the old and the new. The following are some
+examples all assuming `from ruamel import yaml` somewhere at the top of
+your file and some `istream` and `ostream` apropriately opened for
+reading resp. writing.
+
+Loading and dumping using the `SafeLoader`:
+
+ if ruamel.yaml.version_info < (0, 15):
+ data = yaml.safe_load(istream)
+ yaml.safe_dump(data, ostream)
+ else:
+ yml = ruamel.yaml.YAML(typ='safe', pure=True) # 'safe' load and dump
+ data = yml.load(istream)
+ yml.dump(data, ostream)
+
+Loading with the `CSafeLoader`, dumping with `RoundTripLoader`. You need
+two `YAML` instances, but each of them can be re-used:
+--- !python |
+if ruamel.yaml.version_info < (0, 15):
+ data = yaml.load(istream, Loader=yaml.CSafeLoader)
+ yaml.round_trip_dump(data, ostream, width=1000, explicit_start=True)
+else:
+ yml = ruamel.yaml.YAML(typ='safe')
+ data = yml.load(istream)
+ ymlo = ruamel.yaml.YAML() # or yaml.YAML(typ='rt')
+ ymlo.width = 1000
+ ymlo.explicit_start = True
+ ymlo.dump(data, ostream)
+--- |
+Loading and dumping from `pathlib.Path` instances using the
+round-trip-loader:
+--- !code |
+# in myyaml.py
+if ruamel.yaml.version_info < (0, 15):
+ class MyYAML(yaml.YAML):
+ def __init__(self):
+ yaml.YAML.__init__(self)
+ self.preserve_quotes = True
+ self.indent(mapping=4, sequence=4, offset=2)
+# in your code
+try:
+ from myyaml import MyYAML
+except (ModuleNotFoundError, ImportError):
+ if ruamel.yaml.version_info >= (0, 15):
+ raise
+
+# some pathlib.Path
+from pathlib import Path
+inf = Path('/tmp/in.yaml')
+outf = Path('/tmp/out.yaml')
+
+if ruamel.yaml.version_info < (0, 15):
+ with inf.open() as ifp:
+ data = yaml.round_trip_load(ifp, preserve_quotes=True)
+ with outf.open('w') as ofp:
+ yaml.round_trip_dump(data, ofp, indent=4, block_seq_indent=2)
+else:
+ yml = MyYAML()
+ # no need for with statement when using pathlib.Path instances
+ data = yml.load(inf)
+ yml.dump(data, outf)
+--- |
+## Reason for API change
+
+`ruamel.yaml` inherited the way of doing things from `PyYAML`. In
+particular when calling the function `load()` or `dump()` temporary
+instances of `Loader()` resp. `Dumper()` were created that were
+discarded on termination of the function.
+
+This way of doing things leads to several problems:
+
+- it is virtually impossible to return information to the caller apart
+ from the constructed data structure. E.g. if you would get a YAML
+ document version number from a directive, there is no way to let the
+ caller know apart from handing back special data structures. The
+ same problem exists when trying to do on the fly analysis of a
+ document for indentation width.
+
+- these instances were composites of the various load/dump steps and
+ if you wanted to enhance one of the steps, you needed e.g. subclass
+ the emitter and make a new composite (dumper) as well, providing all
+ of the parameters (i.e. copy paste)
+
+ Alternatives, like making a class that returned a `Dumper` when
+ called and sets attributes before doing so, is cumbersome for
+ day-to-day use.
+
+- many routines (like `add_representer()`) have a direct global impact
+ on all of the following calls to `dump()` and those are difficult if
+ not impossible to turn back. This forces the need to subclass
+ `Loaders` and `Dumpers`, a long time problem in PyYAML as some
+ attributes were not `deep_copied` although a bug-report (and fix)
+ had been available a long time.
+
+- If you want to set an attribute, e.g. to control whether literal
+ block style scalars are allowed to have trailing spaces on a line
+ instead of being dumped as double quoted scalars, you have to change
+ the `dump()` family of routines, all of the `Dumpers()` as well as
+ the actual functionality change in `emitter.Emitter()`. The
+ functionality change takes changing 4 (four!) lines in one file, and
+ being able to enable that another 50+ line changes (non-contiguous)
+ in 3 more files resulting in diff that is far over 200 lines long.
+
+- replacing libyaml with something that doesn\'t both support `0o52`
+ and `052` for the integer `42` (instead of `52` as per YAML 1.2) is
+ difficult
+
+With `ruamel.yaml>=0.15.0` the various steps \"know\" about the `YAML`
+instance and can pick up setting, as well as report back information via
+that instance. Representers, etc., are added to a reusable instance and
+different YAML instances can co-exists.
+
+This change eases development and helps prevent regressions.
diff --git a/_doc/basicuse.ryd b/_doc/basicuse.ryd
new file mode 100644
index 0000000..cce50d8
--- /dev/null
+++ b/_doc/basicuse.ryd
@@ -0,0 +1,77 @@
+version: 0.2
+text: md
+pdf: false
+--- !python-pre |
+import sys
+from io import StringIO
+from ruamel.yaml import YAML
+yaml=YAML()
+s = StringIO()
+doc = "a: 1"
+data = dict(a=1)
+--- |
+# Basic Usage
+## Load and dump
+
+You load a YAML document using:
+--- !python |
+from ruamel.yaml import YAML
+
+yaml=YAML(typ='safe') # default, if not specfied, is 'rt' (round-trip)
+yaml.load(doc)
+
+--- |
+in this `doc` can be a file pointer (i.e. an object that has the
+`.read()` method, a string or a `pathlib.Path()`. `typ='safe'`
+accomplishes the same as what `safe_load()` did before: loading of a
+document without resolving unknown tags. Provide `pure=True` to enforce
+using the pure Python implementation, otherwise the faster C libraries
+will be used when possible/available but these behave slightly different
+(and sometimes more like a YAML 1.1 loader).
+
+Dumping works in the same way:
+--- !code |
+from ruamel.yaml import YAML
+
+yaml=YAML()
+yaml.default_flow_style = False
+yaml.dump({'a': [1, 2]}, s)
+--- |
+in this `s` can be a file pointer (i.e. an object that has the
+`.write()` method, or a `pathlib.Path()`. If you want to display your
+output, just stream to `sys.stdout`.
+
+If you need to transform a string representation of the output provide a
+function that takes a string as input and returns one:
+--- !python |
+def tr(s):
+ return s.replace('\n', '<\n') # such output is not valid YAML!
+
+yaml.dump(data, sys.stdout, transform=tr)
+
+--- |
+## More examples
+
+Using the C based SafeLoader (at this time is inherited from
+libyaml/PyYAML and e.g. loads `0o52` as well as `052` as integer
+`42`):
+--- !python |
+ from ruamel.yaml import YAML
+
+ yaml=YAML(typ="safe")
+ yaml.load("""a:\n b: 2\n c: 3\n""")
+
+--- |
+Using the Python based SafeLoader (YAML 1.2 support, `052` loads as
+`52`):
+--- !python |
+ from ruamel.yaml import YAML
+
+ yaml=YAML(typ="safe", pure=True)
+ yaml.load("""a:\n b: 2\n c: 3\n""")
+
+--- |
+
+Restrictions when using the C based SafeLoader/SafeDumper:
+
+- yaml.indent will set the same value for mappings and sequences. (Issue 471)
diff --git a/_doc/conf.py b/_doc/conf.py
new file mode 100644
index 0000000..3b4f6c8
--- /dev/null
+++ b/_doc/conf.py
@@ -0,0 +1,317 @@
+# -*- coding: utf-8 -*-
+#
+# yaml documentation build configuration file, created by
+# sphinx-quickstart on Mon Feb 29 12:03:00 2016.
+#
+# This file is execfile()d with the current directory set to its
+# containing dir.
+#
+# Note that not all possible configuration values are present in this
+# autogenerated file.
+#
+# All configuration values have a default; values that are commented out
+# serve to show the default.
+
+import sys # NOQA
+import os # NOQA
+
+# If extensions (or modules to document with autodoc) are in another directory,
+# add these directories to sys.path here. If the directory is relative to the
+# documentation root, use os.path.abspath to make it absolute, like shown here.
+# sys.path.insert(0, os.path.abspath('.'))
+
+# -- General configuration ------------------------------------------------
+
+# If your documentation needs a minimal Sphinx version, state it here.
+# needs_sphinx = '1.0'
+
+# Add any Sphinx extension module names here, as strings. They can be
+# extensions coming with Sphinx (named 'sphinx.ext.*') or your custom
+# ones.
+extensions = [] # type: ignore
+
+# Add any paths that contain templates here, relative to this directory.
+templates_path = ['_templates']
+
+# The suffix(es) of source filenames.
+# You can specify multiple suffix as a list of string:
+# source_suffix = ['.rst', '.md']
+source_suffix = ['.rst']
+
+# The encoding of source files.
+# source_encoding = 'utf-8-sig'
+
+# The master toctree document.
+master_doc = 'index'
+
+# General information about the project.
+project = 'yaml'
+copyright = '2017-2021, Anthon van der Neut, Ruamel bvba'
+author = 'Anthon van der Neut'
+
+# The version info for the project you are documenting, acts as replacement for
+# |version| and |release|, also used in various other places throughout the
+# built documents.
+#
+try:
+ from ruamel.yaml import __version__, version_info # NOQA
+
+ # The short X.Y version.
+ version = '.'.join([str(ch) for ch in version_info[:3]])
+ # The full version, including alpha/beta/rc tags.
+ release = version # = __version__
+except Exception as e:
+ print('exception', e)
+ version = release = 'dev'
+print('ruamel.yaml version', version)
+# print('cwd:', os.getcwd())
+# current working directory is the one with `conf.py` !
+
+
+class ryd2rst:
+ pass
+
+
+if True:
+ try:
+ from ryd.__main__ import main
+ from pathlib import Path
+
+ oldargv = sys.argv
+ for fn in Path('.').glob('*.ryd'):
+ sys.argv = ['ryd', 'convert', '--no-pdf', str(fn)]
+ main(sys.argv)
+ sys.argv = oldargv
+
+ except Exception as e:
+ print('ryd exception', e)
+ raise
+
+# The language for content autogenerated by Sphinx. Refer to documentation
+# for a list of supported languages.
+#
+# This is also used if you do content translation via gettext catalogs.
+# Usually you set "language" from the command line for these cases.
+language = None
+
+# There are two options for replacing |today|: either, you set today to some
+# non-false value, then it is used:
+# today = ''
+# Else, today_fmt is used as the format for a strftime call.
+# today_fmt = '%B %d, %Y'
+
+# List of patterns, relative to source directory, that match files and
+# directories to ignore when looking for source files.
+exclude_patterns = ['_build']
+
+# The reST default role (used for this markup: `text`) to use for all
+# documents.
+# default_role = None
+
+# If true, '()' will be appended to :func: etc. cross-reference text.
+# add_function_parentheses = True
+
+# If true, the current module name will be prepended to all description
+# unit titles (such as .. function::).
+# add_module_names = True
+
+# If true, sectionauthor and moduleauthor directives will be shown in the
+# output. They are ignored by default.
+# show_authors = False
+
+# The name of the Pygments (syntax highlighting) style to use.
+pygments_style = 'sphinx'
+
+# A list of ignored prefixes for module index sorting.
+# modindex_common_prefix = []
+
+# If true, keep warnings as "system message" paragraphs in the built documents.
+# keep_warnings = False
+
+# If true, `todo` and `todoList` produce output, else they produce nothing.
+todo_include_todos = False
+
+
+# -- Options for HTML output ----------------------------------------------
+
+# The theme to use for HTML and HTML Help pages. See the documentation for
+# a list of builtin themes.
+html_theme = 'default'
+
+# Theme options are theme-specific and customize the look and feel of a theme
+# further. For a list of options available for each theme, see the
+# documentation.
+# html_theme_options = {}
+
+# Add any paths that contain custom themes here, relative to this directory.
+# html_theme_path = []
+
+# The name for this set of Sphinx documents. If None, it defaults to
+# "<project> v<release> documentation".
+html_title = 'Python YAML package documentation'
+
+# A shorter title for the navigation bar. Default is the same as html_title.
+# html_short_title = None
+
+# The name of an image file (relative to this directory) to place at the top
+# of the sidebar.
+# html_logo = None
+
+# The name of an image file (within the static path) to use as favicon of the
+# docs. This file should be a Windows icon file (.ico) being 16x16 or 32x32
+# pixels large.
+# html_favicon = None
+
+# Add any paths that contain custom static files (such as style sheets) here,
+# relative to this directory. They are copied after the builtin static files,
+# so a file named "default.css" will overwrite the builtin "default.css".
+html_static_path = ['_static']
+
+# Add any extra paths that contain custom files (such as robots.txt or
+# .htaccess) here, relative to this directory. These files are copied
+# directly to the root of the documentation.
+# html_extra_path = []
+
+# If not '', a 'Last updated on:' timestamp is inserted at every page bottom,
+# using the given strftime format.
+# html_last_updated_fmt = '%b %d, %Y'
+
+# If true, SmartyPants will be used to convert quotes and dashes to
+# typographically correct entities.
+# html_use_smartypants = True
+
+# Custom sidebar templates, maps document names to template names.
+# html_sidebars = {}
+
+# Additional templates that should be rendered to pages, maps page names to
+# template names.
+# html_additional_pages = {}
+
+# If false, no module index is generated.
+# html_domain_indices = True
+
+# If false, no index is generated.
+# html_use_index = True
+
+# If true, the index is split into individual pages for each letter.
+# html_split_index = False
+
+# If true, links to the reST sources are added to the pages.
+# html_show_sourcelink = True
+
+# If true, "Created using Sphinx" is shown in the HTML footer. Default is True.
+# html_show_sphinx = True
+
+# If true, "(C) Copyright ..." is shown in the HTML footer. Default is True.
+# html_show_copyright = True
+
+# If true, an OpenSearch description file will be output, and all pages will
+# contain a <link> tag referring to it. The value of this option must be the
+# base URL from which the finished HTML is served.
+# html_use_opensearch = ''
+
+# This is the file name suffix for HTML files (e.g. ".xhtml").
+# html_file_suffix = None
+
+# Language to be used for generating the HTML full-text search index.
+# Sphinx supports the following languages:
+# 'da', 'de', 'en', 'es', 'fi', 'fr', 'hu', 'it', 'ja'
+# 'nl', 'no', 'pt', 'ro', 'ru', 'sv', 'tr'
+# html_search_language = 'en'
+
+# A dictionary with options for the search language support, empty by default.
+# Now only 'ja' uses this config value
+# html_search_options = {'type': 'default'}
+
+# The name of a javascript file (relative to the configuration directory) that
+# implements a search results scorer. If empty, the default will be used.
+# html_search_scorer = 'scorer.js'
+
+# Output file base name for HTML help builder.
+htmlhelp_basename = 'yamldoc'
+
+# -- Options for LaTeX output ---------------------------------------------
+
+latex_elements = {
+ # The paper size ('letterpaper' or 'a4paper').
+ 'papersize': 'a4paper',
+ # The font size ('10pt', '11pt' or '12pt').
+ # 'pointsize': '10pt',
+ # Additional stuff for the LaTeX preamble.
+ # 'preamble': '',
+ # Latex figure (float) alignment
+ # 'figure_align': 'htbp',
+}
+
+# Grouping the document tree into LaTeX files. List of tuples
+# (source start file, target name, title,
+# author, documentclass [howto, manual, or own class]).
+latex_documents = [
+ (
+ master_doc,
+ 'yaml.tex',
+ 'Python YAML package documentation',
+ 'Anthon van der Neut',
+ 'manual',
+ ),
+]
+
+# The name of an image file (relative to this directory) to place at the top of
+# the title page.
+# latex_logo = None
+
+# For "manual" documents, if this is true, then toplevel headings are parts,
+# not chapters.
+# latex_use_parts = False
+
+# If true, show page references after internal links.
+# latex_show_pagerefs = False
+
+# If true, show URL addresses after external links.
+# latex_show_urls = False
+
+# Documents to append as an appendix to all manuals.
+# latex_appendices = []
+
+# If false, no module index is generated.
+# latex_domain_indices = True
+
+
+# -- Options for manual page output ---------------------------------------
+
+# One entry per manual page. List of tuples
+# (source start file, name, description, authors, manual section).
+man_pages = [(master_doc, 'yaml', 'yaml Documentation', [author], 1)]
+
+# If true, show URL addresses after external links.
+# man_show_urls = False
+
+
+# -- Options for Texinfo output -------------------------------------------
+
+# Grouping the document tree into Texinfo files. List of tuples
+# (source start file, target name, title, author,
+# dir menu entry, description, category)
+texinfo_documents = [
+ (
+ master_doc,
+ 'yaml',
+ 'yaml Documentation',
+ author,
+ 'yaml',
+ 'One line description of project.',
+ 'Miscellaneous',
+ ),
+]
+
+# Documents to append as an appendix to all manuals.
+# texinfo_appendices = []
+
+# If false, no module index is generated.
+# texinfo_domain_indices = True
+
+# How to display URL addresses: 'footnote', 'no', or 'inline'.
+# texinfo_show_urls = 'footnote'
+
+# If true, do not generate a @detailmenu in the "Top" node's menu.
+# texinfo_no_detailmenu = False
diff --git a/_doc/contributing.ryd b/_doc/contributing.ryd
new file mode 100644
index 0000000..24e351b
--- /dev/null
+++ b/_doc/contributing.ryd
@@ -0,0 +1,139 @@
+version: 0.2
+text: md
+pdf: false
+--- |
+# Contributing
+
+Any contribution to `ruamel.yaml` is welcome, be it in the form of an
+email, a question on stackoverflow (I\'ll get notified of that when you
+tag it with `ruamel.yaml`), an issue or pull-request (PR) on
+sourceforge.
+
+Contributing via stackoverflow is, for most, easy to do. When I answer
+your question there and the answer warrants an extension to the
+documentation or code, I will include it in a documentation update
+and/or future (normally the next) release of `ruamel.yaml`.
+
+Please don\'t post support questions as an issue on sourceforge.
+
+## Documentation
+
+The documentation for `ruamel.yaml` is in YAML, more specifically in
+[ryd](https://pypi.python.org/pypi/ryd) ( /rɑɪt/, pronounced like the
+verb "write" ). This is Markdown (previously reStructuredText)
+mixed with Python, each in
+separate YAML documents within a single file. If you know a bit of YAML,
+Python and Markdown, it will be clear how that works.
+
+If you want to contribute to the documentation, you can send me a clear
+description of the needed changes, e.g. as a unified diff. If the
+changes encompass multiple documents in a `.ryd` file, it is best to
+install `ryd` (use a virtualenv!), clone the `ruamel.yaml` repository on
+sourceforge, edit documentation, run `ryd`:
+
+ ryd --pdf '**/*.ryd'
+
+(quoting might not be necessary depending on your shell), and once the
+PDF(s) look acceptable, submit a pull-request.
+
+`ryd` will check your file for single backquotes (my most common mistake
+going back and forth between reStructuredText and other mark up).
+
+If you contribute example programs, note that `ryd` will automatically
+run your program (so it should be correct) and can include the output of
+the program in the resulting `.rst` (and PDF) file.
+
+## Code
+
+Code changes are welcome as well, but anything beyond a minor change
+should be tested (`tox`/`pytest`), checked for typing conformance
+(`mypy`) and pass pep8 conformance (`flake8`).
+
+In my experience it is best to use two `virtualenv` environments, one
+with the latest Python version currently supported, the other with
+the oldest supported version.
+In the site-packages directory of each virtualenv make a soft link to
+the ruamel directory of your (cloned and checked out) copy of the
+repository. Do not under any circumstances run `pip install -e .` or
+`python setup.py -e .` it will not work (at least not until these
+commands are fixed to support packages with namespaces).
+
+You can install `tox`, `pytest`, `mypy` and `flake8` in the Python3
+`virtualenv`, or in a `virtualenv` of their own. If all of these
+commands pass without warning/error, you can create your pull-request.
+
+### Flake
+
+My `~/.config/flake8` file:
+
+ [flake8]
+ show-source = True
+ max-line-length = 95
+ ignore = F405
+
+The suppress of F405 is necessary to allow `from xxx import *`, which I
+have not removed in all places (yet).
+
+First make sure your checked out source passes `flake8` without test (it
+should). Then make your changes pass without any warnings/errors.
+
+### Tox/pytest
+
+Whether you add something or fix some bug with your code changes, first
+add one or more tests that fail in the unmodified source when running
+`tox`. Once that is in place add your code, which should have as a
+result that your added test(s) no longer fail, and neither should any
+other existing tests.
+
+### Typing/mypy
+
+If you add methods or functions to `ruamel.yaml`, you will need to add
+Python 2.7 compatible typing information in order for `mypy` to pass
+without error.
+
+I run `mypy` from the directory where the (link to) ruamel directory is
+using:
+
+ mypy --py2 --strict --follow-imports silent ruamel/yaml/*.py
+
+This should give no errors or warnings
+
+## Generated files
+
+I use a minimal environment when developing, void of most artifacts
+needed for packaging, testing etc. These artifact files are *generated*,
+just before committing to sourceforge and pushing to PyPI, with nuances
+coming from the `_package_data` information in `__init__.py`. Included
+changes in these files will automatically be reverted, even assuming
+your PR is accepted as is.
+
+Consider the following files **read-only** (if you think changes need to
+be made to these, contact me):
+
+ setup.py
+ tox.ini
+ LICENSE
+ _ryd/conf.py
+ -ryd/Makefile
+
+## Vulnerabilities
+
+If you find a vulnerability in `ruamel.yaml` (e.g. that would show the
+`safe` and `rt` loader are not safe due to a bug in the software)),
+please contact me directly via email, or by leaving a comment on
+StackOverflow (below any of my posts), without going into the details
+about the vulnerability. After contact is estabilished I will work to
+eliminate the vulnerability in a timely fashion. After the vulnerability
+is removed, and affected parties haven been notified to allow them to
+update versions, the vulnerability will be published, and your role in
+finding/resolving this properly attributed.
+
+Please note that there is a CVE out there against `ruamel.yaml`, that states
+that the input of the function `load()` is not checked. As the
+use of `ruamel.yaml.load()` was never the default, was documented to potentially
+cause problems when specific parameters were provided, and issued a
+warning, this was always an inappropriate statement.
+(To compare: no such CVE was given for the use of the Python standard library
+function `pickle.load`, which only documents which is default function
+to use and only documented to potentially dangerious). The whole CVE is moot,
+with the removal of the `load()` function 0.18.
diff --git a/_doc/detail.ryd b/_doc/detail.ryd
new file mode 100644
index 0000000..26ce7f0
--- /dev/null
+++ b/_doc/detail.ryd
@@ -0,0 +1,298 @@
+version: 0.2
+text: md
+pdf: false
+--- |
+# Details
+
+- support for simple lists as mapping keys by transforming these to
+ tuples
+- `!!omap` generates ordereddict (C) on Python 2,
+ collections.OrderedDict on Python 3, and `!!omap` is generated for
+ these types.
+- Tests whether the C yaml library is installed as well as the header
+ files. That library doesn\'t generate CommentTokens, so it cannot be
+ used to do round trip editing on comments. It can be used to speed
+ up normal processing (so you don\'t need to install `ruamel.yaml`
+ and `PyYaml`). See the section *Optional requirements*.
+- Basic support for multiline strings with preserved newlines and
+ chomping ( \'`|`\', \'`|+`\', \'`|-`\' ). As this subclasses the
+ string type the information is lost on reassignment. (This might be
+ changed in the future so that the preservation/folding/chomping is
+ part of the parent container, like comments).
+- anchors names that are hand-crafted (not of the form`idNNN`) are
+ preserved
+- [merges](http://yaml.org/type/merge.html) in dictionaries are
+ preserved
+- adding/replacing comments on block-style sequences and mappings with
+ smart column positioning
+- collection objects (when read in via RoundTripParser) have an `lc`
+ property that contains line and column info `lc.line` and `lc.col`.
+ Individual positions for mappings and sequences can also be
+ retrieved (`lc.key('a')`, `lc.value('a')` resp. `lc.item(3)`)
+- preservation of whitelines after block scalars. Contributed by Sam
+ Thursfield.
+
+*In the following examples it is assumed you have done something like:*:
+
+ from ruamel.yaml import YAML
+ yaml = YAML()
+
+*if not explicitly specified.*
+
+## Indentation of block sequences
+
+Although ruamel.yaml doesn\'t preserve individual indentations of block
+sequence items, it does properly dump:
+
+ x:
+ - b: 1
+ - 2
+
+back to:
+
+ x:
+ - b: 1
+ - 2
+
+if you specify `yaml.indent(sequence=4)` (indentation is counted to the
+beginning of the sequence element).
+
+PyYAML (and older versions of ruamel.yaml) gives you non-indented
+scalars (when specifying default_flow_style=False):
+
+ x:
+ - b: 1
+ - 2
+
+You can use `mapping=4` to also have the mappings values indented. The
+dump also observes an additional `offset=2` setting that can be used to
+push the dash inwards, *within the space defined by* `sequence`.
+
+The above example with the often seen
+`yaml.indent(mapping=2, sequence=4, offset=2)` indentation:
+
+ x:
+ y:
+ - b: 1
+ - 2
+
+The defaults are as if you specified
+`yaml.indent(mapping=2, sequence=2, offset=0)`.
+
+If the `offset` equals `sequence`, there is not enough room for the dash
+and the space that has to follow it. In that case the element itself
+would normally be pushed to the next line (and older versions of
+`ruamel.yaml` did so). But this is prevented from happening. However the
+`indent` level is what is used for calculating the cumulative indent for
+deeper levels and specifying `sequence=3` resp. `offset=2`, might give
+correct, but counter-intuitive results.
+
+**It is best to always have** `sequence >= offset + 2` **but this is not
+enforced**. Depending on your structure, not following this advice
+**might lead to invalid output**.
+
+### Inconsistently indented YAML
+
+If your input is inconsistently indented, such indentation cannot be
+preserved. The first round-trip will make it consistent/normalize it.
+Here are some inconsistently indented YAML examples.
+
+`b` indented 3, `c` indented 4 positions:
+
+ a:
+ b:
+ c: 1
+
+Top level sequence is indented 2 without offset, the other sequence 4
+(with offset 2):
+
+ - key:
+ - foo
+ - bar
+
+### Indenting using `typ="safe"`
+
+The C based emitter doesn't have the fine control, distinguishing between
+block mappings and sequences. Do only use the `pure` Python versions
+of the dumper if you want to have that sort of control.
+
+
+## Positioning ':' in top level mappings, prefixing ':'
+
+If you want your toplevel mappings to look like:
+
+ library version: 1
+ comment : |
+ this is just a first try
+
+then set `yaml.top_level_colon_align = True` (and `yaml.indent = 4`).
+`True` causes calculation based on the longest key, but you can also
+explicitly set a number.
+
+If you want an extra space between a mapping key and the colon specify
+`yaml.prefix_colon = ' '`:
+
+ - https://myurl/abc.tar.xz : 23445
+ # ^ extra space here
+ - https://myurl/def.tar.xz : 944
+
+If you combine `prefix_colon` with `top_level_colon_align`, the top
+level mapping doesn\'t get the extra prefix. If you want that anyway,
+specify `yaml.top_level_colon_align = 12` where `12` has to be an
+integer that is one more than length of the widest key.
+
+### Document version support
+
+In YAML a document version can be explicitly set by using:
+
+ %YAML 1.x
+
+before the document start (at the top or before a `---`). For
+`ruamel.yaml` x has to be 1 or 2. If no explicit version is set [version
+1.2](http://www.yaml.org/spec/1.2/spec.html) is assumed (which has been
+released in 2009).
+
+The 1.2 version does **not** support:
+
+- sexagesimals like `12:34:56`
+- octals that start with 0 only: like `012` for number 10 (`0o12`
+ **is** supported by YAML 1.2)
+- Unquoted Yes and On as alternatives for True and No and Off for
+ False.
+
+If you cannot change your YAML files and you need them to load as 1.1
+you can load with `yaml.version = (1, 1)`, or the equivalent (version
+can be a tuple, list or string) `yaml.version = "1.1"`
+
+*If you cannot change your code, stick with ruamel.yaml==0.10.23 and let
+me know if it would help to be able to set an environment variable.*
+
+This does not affect dump as ruamel.yaml never emitted sexagesimals, nor
+octal numbers, and emitted booleans always as true resp. false
+
+### Round trip including comments
+
+The major motivation for this fork is the round-trip capability for
+comments. The integration of the sources was just an initial step to
+make this easier.
+
+#### adding/replacing comments
+
+Starting with version 0.8, you can add/replace comments on block style
+collections (mappings/sequences resuting in Python dict/list). The basic
+for for this is:
+--- !python |
+ from __future__ import print_function
+
+ import sys
+ import ruamel.yaml
+
+ yaml = ruamel.yaml.YAML() # defaults to round-trip
+
+ inp = """\
+ abc:
+ - a # comment 1
+ xyz:
+ a: 1 # comment 2
+ b: 2
+ c: 3
+ d: 4
+ e: 5
+ f: 6 # comment 3
+ """
+
+ data = yaml.load(inp)
+ data['abc'].append('b')
+ data['abc'].yaml_add_eol_comment('comment 4', 1) # takes column of comment 1
+ data['xyz'].yaml_add_eol_comment('comment 5', 'c') # takes column of comment 2
+ data['xyz'].yaml_add_eol_comment('comment 6', 'e') # takes column of comment 3
+ data['xyz'].yaml_add_eol_comment('comment 7\n\n# that\'s all folks', 'd', column=20)
+
+ yaml.dump(data, sys.stdout)
+--- !stdout |
+Resulting in::
+--- !comment |
+ abc:
+ - a # comment 1
+ - b # comment 4
+ xyz:
+ a: 1 # comment 2
+ b: 2
+ c: 3 # comment 5
+ d: 4 # comment 7
+ e: 5 # comment 6
+ f: 6 # comment 3
+
+--- |
+If the comment doesn\'t start with \'#\', this will be added. The key is
+the element index for list, the actual key for dictionaries. As can be
+seen from the example, the column to choose for a comment is derived
+from the previous, next or preceding comment column (picking the first
+one found).
+
+Make sure that the added comment is correct, in the sense that when it
+contains newlines, the following is either an empty line or a line with
+only spaces, or the first non-space is a `#`.
+
+# Config file formats
+
+There are only a few configuration file formats that are easily readable
+and editable: JSON, INI/ConfigParser, YAML (XML is to cluttered to be
+called easily readable).
+
+Unfortunately [JSON](http://www.json.org/) doesn\'t support comments,
+and although there are some solutions with pre-processed filtering of
+comments, there are no libraries that support round trip updating of
+such commented files.
+
+INI files support comments, and the excellent
+[ConfigObj](http://www.voidspace.org.uk/python/configobj.html) library
+by Foord and Larosa even supports round trip editing with comment
+preservation, nesting of sections and limited lists (within a value).
+Retrieval of particular value format is explicit (and extensible).
+
+YAML has basic mapping and sequence structures as well as support for
+ordered mappings and sets. It supports scalars various types including
+dates and datetimes (missing in JSON). YAML has comments, but these are
+normally thrown away.
+
+Block structured YAML is a clean and very human readable format. By
+extending the Python YAML parser to support round trip preservation of
+comments, it makes YAML a very good choice for configuration files that
+are human readable and editable while at the same time interpretable and
+modifiable by a program.
+
+# Extending
+
+There are normally six files involved when extending the roundtrip
+capabilities: the reader, parser, composer and constructor to go from
+YAML to Python and the resolver, representer, serializer and emitter to
+go the other way.
+
+Extending involves keeping extra data around for the next process step,
+eventuallly resulting in a different Python object (subclass or
+alternative), that should behave like the original, but on the way from
+Python to YAML generates the original (or at least something much
+closer).
+
+# Smartening
+
+When you use round-tripping, then the complex data you get are already
+subclasses of the built-in types. So you can patch in extra methods or
+override existing ones. Some methods are already included and you can
+do:
+
+ yaml_str = """\
+ a:
+ - b:
+ c: 42
+ - d:
+ f: 196
+ e:
+ g: 3.14
+ """
+
+
+ data = yaml.load(yaml_str)
+
+ assert data.mlget(['a', 1, 'd', 'f'], list_ok=True) == 196
diff --git a/_doc/dumpcls.ryd b/_doc/dumpcls.ryd
new file mode 100644
index 0000000..048cdeb
--- /dev/null
+++ b/_doc/dumpcls.ryd
@@ -0,0 +1,315 @@
+version: 0.2
+text: md
+pdf: false
+# code_directory: ../_example
+--- |
+# Working with Python classes
+
+## Dumping Python classes
+
+Only `yaml = YAML(typ='unsafe')` loads and dumps Python objects
+out-of-the-box. And since it loads **any** Python object, this can be
+unsafe, so don't use it.
+
+If you have instances of some class(es) that you want to dump or load,
+it is easy to allow the YAML instance to do that explicitly. You can
+either register the class with the `YAML` instance or decorate the
+class.
+
+Registering is done with `YAML.register_class()`:
+--- !python |
+
+import sys
+import ruamel.yaml
+
+
+class User:
+ def __init__(self, name, age):
+ self.name = name
+ self.age = age
+
+
+yaml = ruamel.yaml.YAML()
+yaml.register_class(User)
+yaml.dump([User('Anthon', 18)], sys.stdout)
+--- !stdout |
+which gives as output::
+--- |
+The tag `!User` originates from the name of the class.
+
+You can specify a different tag by adding the attribute `yaml_tag`, and
+explicitly specify dump and/or load *classmethods* which have to be
+named `to_yaml` resp. `from_yaml`:
+--- !python |
+import sys
+import ruamel.yaml
+
+
+class User:
+ yaml_tag = u'!user'
+
+ def __init__(self, name, age):
+ self.name = name
+ self.age = age
+
+ @classmethod
+ def to_yaml(cls, representer, node):
+ return representer.represent_scalar(cls.yaml_tag,
+ u'{.name}-{.age}'.format(node, node))
+
+ @classmethod
+ def from_yaml(cls, constructor, node):
+ return cls(*node.value.split('-'))
+
+
+yaml = ruamel.yaml.YAML()
+yaml.register_class(User)
+yaml.dump([User('Anthon', 18)], sys.stdout)
+--- !stdout |
+which gives as output::
+
+--- |
+When using the decorator, which takes the `YAML()` instance as a
+parameter, the `yaml = YAML()` line needs to be moved up in the file:
+--- !python |
+import sys
+from ruamel.yaml import YAML, yaml_object
+
+yaml = YAML()
+
+
+@yaml_object(yaml)
+class User:
+ yaml_tag = u'!user'
+
+ def __init__(self, name, age):
+ self.name = name
+ self.age = age
+
+ @classmethod
+ def to_yaml(cls, representer, node):
+ return representer.represent_scalar(cls.yaml_tag,
+ u'{.name}-{.age}'.format(node, node))
+
+ @classmethod
+ def from_yaml(cls, constructor, node):
+ return cls(*node.value.split('-'))
+
+
+yaml.dump([User('Anthon', 18)], sys.stdout)
+
+--- |
+The `yaml_tag`, `from_yaml` and `to_yaml` work in the same way as when
+using `.register_class()`.
+
+Alternatively you can use the `register_class()` method as decorator,
+This also requires you have the yaml instance available:
+--- !python |
+import sys
+import ruamel.yaml
+
+yaml = ruamel.yaml.YAML()
+
+@yaml.register_class
+class User:
+ yaml_tag = u'!user'
+
+ def __init__(self, name, age):
+ self.name = name
+ self.age = age
+
+ @classmethod
+ def to_yaml(cls, representer, node):
+ return representer.represent_scalar(cls.yaml_tag,
+ u'{.name}-{.age}'.format(node, node))
+
+ @classmethod
+ def from_yaml(cls, constructor, node):
+ return cls(*node.value.split('-'))
+
+
+yaml.dump([User('Anthon', 18)], sys.stdout)
+
+--- !stdout |
+
+This also gives:
+
+--- |
+
+If your class is dumped as a YAML mapping or sequence, there might be an (indirect)
+reference to the object itself in one or more of the mapping keys (in YAML these
+don't have to be simple scalars), mapping values or sequence entries.
+
+That means that re-creating an object in `to_yaml` cannot generally just create
+a `dict`/`list` from the `node` parameter and then create and return a complete
+object. The solution for this is to create an empty object and yield that
+and then fill in the content data afterwards. That way, if there is a self
+reference, and the same node is encountered *while creating the content for the
+object*, there is an `id` (from the yielded object) created for that node which
+can be assigned.
+
+--- !python |
+
+from pathlib import Path
+import ruamel.yaml
+
+class Person:
+ def __init__(self, name, siblings=None):
+ self.name = name
+ self.siblings = [] if siblings is None else siblings
+
+arya = Person('Arya')
+sansa = Person('Sansa')
+arya.siblings.append(sansa) # there are better ways to represent this
+sansa.siblings.append(arya)
+
+yaml = ruamel.yaml.YAML()
+yaml.register_class(Person)
+
+path = Path('/tmp/arya.yaml')
+yaml.dump(arya, path)
+print(path.read_text())
+
+--- !stdout |
+
+dumping as:
+
+--- |
+
+And you can load the output:
+
+--- !python |
+
+from pathlib import Path
+import ruamel.yaml
+
+class Person:
+ def __init__(self, name, siblings=None):
+ self.name = name
+ self.siblings = [] if siblings is None else siblings
+
+ def __repr__(self):
+ return f'Person(name: {self.name}, siblings: {self.siblings})'
+
+path = Path('/tmp/arya.yaml')
+yaml = ruamel.yaml.YAML()
+yaml.register_class(Person)
+data = yaml.load(path)
+
+print(data)
+
+--- !stdout |
+
+giving:
+--- |
+
+But if you provide a (to) simple loader:
+
+--- !python |
+
+from pathlib import Path
+import ruamel.yaml
+
+class Person:
+ def __init__(self, name, siblings=None):
+ self.name = name
+ self.siblings = [] if siblings is None else siblings
+
+ def __repr__(self):
+ return f'Person(name: {self.name}, siblings: {self.siblings})'
+
+ @classmethod
+ def from_yaml(cls, constructor, node):
+ data = ruamel.yaml.CommentedMap()
+ constructor.construct_mapping(node, maptyp=data, deep=True)
+ return cls(**data)
+
+
+path = Path('/tmp/arya.yaml')
+yaml = ruamel.yaml.YAML()
+yaml.register_class(Person)
+data = yaml.load(path)
+print(data)
+
+--- !stdout |
+
+giving:
+
+--- |
+As you can see, Sansa has no normal siblings after this load.
+
+What you need to do is yield the empty Person instance and fill it in
+afterwards:
+
+--- !python |
+
+from pathlib import Path
+import ruamel.yaml
+
+class Person:
+ def __init__(self, name, siblings=None):
+ self.name = name
+ self.siblings = [] if siblings is None else siblings
+
+ def __repr__(self):
+ return f'Person(name: {self.name}, siblings: {self.siblings})'
+
+ @classmethod
+ def from_yaml(cls, constructor, node):
+ person = Person(name='')
+ yield person
+ data = ruamel.yaml.CommentedMap()
+ constructor.construct_mapping(node, maptyp=data, deep=True)
+ for k, v in data.items():
+ setattr(person, k, v)
+
+
+path = Path('/tmp/arya.yaml')
+yaml = ruamel.yaml.YAML()
+yaml.register_class(Person)
+data = yaml.load(path)
+print(data)
+
+--- !stdout |
+
+giving:
+
+--- |
+
+## Dataclass
+
+Although you could always register dataclasses, in 0.17.34 support was added to
+call `__post_init__()` on these classes, if available.
+
+
+--- !python |
+
+from typing import ClassVar
+from dataclasses import dataclass
+import ruamel.yaml
+
+@dataclass
+class DC:
+ yaml_tag: ClassVar = '!dc_example' # if you don't want !DC as tag
+ abc: int
+ klm: int
+ xyz: int = 0
+
+ def __post_init__(self) -> None:
+ self.xyz = self.abc + self.klm
+
+yaml = ruamel.yaml.YAML()
+yaml.register_class(DC)
+dc = DC(abc=5, klm=42)
+assert dc.xyz == 47
+
+yaml_str = """\
+!dc_example
+abc: 13
+klm: 37
+"""
+dc2 = yaml.load(yaml_str)
+print(f'{dc2.xyz=}')
+
+--- !stdout |
+printing:
diff --git a/_doc/example.ryd b/_doc/example.ryd
new file mode 100644
index 0000000..4b431cd
--- /dev/null
+++ b/_doc/example.ryd
@@ -0,0 +1,255 @@
+version: 0.2
+text: md
+pdf: false
+--- |
+# Examples
+
+Basic round trip of parsing YAML to Python objects, modifying and
+generating YAML:
+--- !python |
+ import sys
+ from ruamel.yaml import YAML
+
+ inp = """\
+ # example
+ name:
+ # details
+ family: Smith # very common
+ given: Alice # one of the siblings
+ """
+
+ yaml = YAML()
+ code = yaml.load(inp)
+ code['name']['given'] = 'Bob'
+
+ yaml.dump(code, sys.stdout)
+
+--- !stdout |
+Resulting in::
+--- |
+------------------------------------------------------------------------
+
+YAML handcrafted anchors and references as well as key merging are
+preserved. The merged keys can transparently be accessed using `[]` and
+`.get()`:
+--- !python |
+ from ruamel.yaml import YAML
+
+ inp = """\
+ - &CENTER {x: 1, y: 2}
+ - &LEFT {x: 0, y: 2}
+ - &BIG {r: 10}
+ - &SMALL {r: 1}
+ # All the following maps are equal:
+ # Explicit keys
+ - x: 1
+ y: 2
+ r: 10
+ label: center/big
+ # Merge one map
+ - <<: *CENTER
+ r: 10
+ label: center/big
+ # Merge multiple maps
+ - <<: [*CENTER, *BIG]
+ label: center/big
+ # Override
+ - <<: [*BIG, *LEFT, *SMALL]
+ x: 1
+ label: center/big
+ """
+
+ yaml = YAML()
+ data = yaml.load(inp)
+ assert data[7]['y'] == 2
+--- |
+The `CommentedMap`, which is the `dict` like construct one gets when
+round-trip loading, supports insertion of a key into a particular
+position, while optionally adding a comment:
+--- !python |
+ import sys
+ from ruamel.yaml import YAML
+
+ yaml_str = """\
+ first_name: Art
+ occupation: Architect # This is an occupation comment
+ about: Art Vandelay is a fictional character that George invents...
+ """
+
+ yaml = YAML()
+ data = yaml.load(yaml_str)
+ data.insert(1, 'last name', 'Vandelay', comment="new key")
+ yaml.dump(data, sys.stdout)
+
+--- !stdout |
+gives::
+--- |
+Please note that the comment is aligned with that of its neighbour (if
+available).
+
+The above was inspired by a
+[question](http://stackoverflow.com/a/36970608/1307905) posted by
+*demux* on StackOverflow.
+
+------------------------------------------------------------------------
+
+By default `ruamel.yaml` indents with two positions in block style, for
+both mappings and sequences. For sequences the indent is counted to the
+beginning of the scalar, with the dash taking the first position of the
+indented \"space\".
+
+You can change this default indentation by e.g. using `yaml.indent()`:
+--- !python |
+
+import sys
+from ruamel.yaml import YAML
+
+d = dict(a=dict(b=2),c=[3, 4])
+yaml = YAML()
+yaml.dump(d, sys.stdout)
+print('0123456789')
+yaml = YAML()
+yaml.indent(mapping=4, sequence=6, offset=3)
+yaml.dump(d, sys.stdout)
+print('0123456789')
+
+
+--- !stdout |
+
+giving::
+
+
+--- |
+If a block sequence or block mapping is the element of a sequence, the
+are, by default, displayed
+[compact](http://yaml.org/spec/1.2/spec.html#id2797686) notation. This
+means that the dash of the \"parent\" sequence is on the same line as
+the first element resp. first key/value pair of the child collection.
+
+If you want either or both of these (sequence within sequence, mapping
+within sequence) to begin on the next line use `yaml.compact()`:
+--- !python |
+
+import sys
+from ruamel.yaml import YAML
+
+d = [dict(b=2), [3, 4]]
+yaml = YAML()
+yaml.dump(d, sys.stdout)
+print('='*15)
+yaml = YAML()
+yaml.compact(seq_seq=False, seq_map=False)
+yaml.dump(d, sys.stdout)
+
+
+--- !stdout |
+
+giving::
+
+
+--- |
+------------------------------------------------------------------------
+
+The following program uses three dumps on the same data, resulting in a
+stream with three documents:
+--- !python |
+import sys
+from ruamel.yaml import YAML
+
+data = {1: {1: [{1: 1, 2: 2}, {1: 1, 2: 2}], 2: 2}, 2: 42}
+
+yaml = YAML()
+yaml.explicit_start = True
+yaml.dump(data, sys.stdout)
+yaml.indent(sequence=4, offset=2)
+yaml.dump(data, sys.stdout)
+
+
+def sequence_indent_four(s):
+ # this will fail on direclty nested lists: {1; [[2, 3], 4]}
+ levels = []
+ ret_val = ''
+ for line in s.splitlines(True):
+ ls = line.lstrip()
+ indent = len(line) - len(ls)
+ if ls.startswith('- '):
+ if not levels or indent > levels[-1]:
+ levels.append(indent)
+ elif levels:
+ if indent < levels[-1]:
+ levels = levels[:-1]
+ # same -> do nothing
+ else:
+ if levels:
+ if indent <= levels[-1]:
+ while levels and indent <= levels[-1]:
+ levels = levels[:-1]
+ ret_val += ' ' * len(levels) + line
+ return ret_val
+
+yaml = YAML()
+yaml.explicit_start = True
+yaml.dump(data, sys.stdout, transform=sequence_indent_four)
+
+--- !stdout |
+gives as output::
+
+--- |
+The transform example, in the last document, was inspired by a [question
+posted by \*nowox\*](https://stackoverflow.com/q/44388701/1307905) on
+StackOverflow.
+
+------------------------------------------------------------------------
+
+## Output of `dump()` as a string
+
+The single most abused "feature" of the old API is not providing the
+(second) stream parameter to one of the `dump()` variants, in order to
+get a monolithic string representation of the stream back.
+
+Apart from being memory inefficient and slow, quite often people using
+this did not realise that `print(round_trip_dump(dict(a=1, b=2)))` gets
+you an extra, empty, line after `b: 2`.
+
+The real question is why this functionality, which is seldom really
+necessary, is available in the old API (and in PyYAML) in the first
+place. One explanation you get by looking at what someone would need to
+do to make this available if it weren\'t there already. Apart from
+subclassing the `Serializer` and providing a new `dump` method, which
+would ten or so lines, another **hundred** lines, essentially the whole
+`dumper.py` file, would need to be copied and to make use of this
+serializer.
+
+The fact is that one should normally be doing
+`round_trip_dump(dict(a=1, b=2)), sys.stdout)` and do away with 90% of
+the cases for returning the string, and that all post-processing YAML,
+before writing to stream, can be handled by using the `transform=`
+parameter of dump, being able to handle most of the rest. But it is also
+much easier in the new API to provide that YAML output as a string if
+you really need to have it (or think you do):
+--- !python |
+import sys
+from ruamel.yaml import YAML
+from ruamel.yaml.compat import StringIO
+
+class MyYAML(YAML):
+ def dump(self, data, stream=None, **kw):
+ inefficient = False
+ if stream is None:
+ inefficient = True
+ stream = StringIO()
+ YAML.dump(self, data, stream, **kw)
+ if inefficient:
+ return stream.getvalue()
+
+yaml = MyYAML() # or typ='safe'/'unsafe' etc
+--- |
+with about one tenth of the lines needed for the old interface, you can
+once more do:
+--- !code |
+print(yaml.dump(dict(a=1, b=2)))
+--- |
+instead of:
+--- !code |
+yaml.dump((dict(a=1, b=2)), sys.stdout)
+print() # or sys.stdout.write('\n')
diff --git a/_doc/install.ryd b/_doc/install.ryd
new file mode 100644
index 0000000..9edfc70
--- /dev/null
+++ b/_doc/install.ryd
@@ -0,0 +1,47 @@
+version: 0.2
+text: md
+pdf: false
+--- |
+# Installing
+
+Make sure you have a recent version of `pip` and `setuptools` installed.
+The later needs environment marker support (`setuptools>=20.6.8`) and
+that is e.g. bundled with Python 3.4.6 but not with 3.4.4. It is
+probably best to do:
+
+ pip install -U pip setuptools wheel
+
+in your environment (`virtualenv`, (Docker) container, etc) before
+installing `ruamel.yaml`.
+
+`ruamel.yaml` itself should be installed from [PyPI]() using:
+
+ pip install ruamel.yaml
+
+If you want to process jinja2/YAML templates (which are not valid YAML
+with the default jinja2 markers), do `pip install ruamel.yaml[jinja2]`
+(you might need to quote the last argument because of the `[]`)
+
+There also is a commandline utility `yaml` available after installing:
+
+ pip install ruamel.yaml.cmd
+
+that allows for round-trip testing/re-indenting and conversion of YAML
+files (JSON,INI,HTML tables)
+
+## Optional requirements
+
+If you have the the header files for your Python executables installed
+then you can use the (non-roundtrip), but faster, C loader and emitter.
+
+On Debian systems you should use:
+
+ sudo apt-get install python3-dev
+
+you can leave out `python3-dev` if you don\'t use python3
+
+For CentOS (7) based systems you should do:
+
+ sudo yum install python-devel
+--- !inc-raw |
+links.rydinc
diff --git a/_doc/links.rydinc b/_doc/links.rydinc
new file mode 100644
index 0000000..7e840c7
--- /dev/null
+++ b/_doc/links.rydinc
@@ -0,0 +1,7 @@
+
+.. _tox: https://pypi.python.org/pypi/tox
+.. _py.test: http://pytest.org/latest/
+.. _YAML 1.1: http://www.yaml.org/spec/1.1/spec.html
+.. _YAML 1.2: http://www.yaml.org/spec/1.2/spec.html
+.. _PyPI: https://pypi.python.org/pypi
+.. _ruamel.yaml: https://pypi.python.org/pypi/ruamel.yaml
diff --git a/_doc/overview.ryd b/_doc/overview.ryd
new file mode 100644
index 0000000..b10dc78
--- /dev/null
+++ b/_doc/overview.ryd
@@ -0,0 +1,47 @@
+version: 0.2
+text: md
+pdf: false
+--- |
+# Overview
+
+`ruamel.yaml` is a YAML 1.2 loader/dumper package for Python. It is a
+derivative of Kirill Simonov\'s [PyYAML
+3.11](https://bitbucket.org/xi/pyyaml).
+
+`ruamel.yaml` supports [YAML 1.2]() and has round-trip loaders and
+dumpers. A round-trip is a YAML load-modify-save sequence and
+ruamel.yaml tries to preserve, among others:
+
+- comments
+- block style and key ordering are kept, so you can diff the
+ round-tripped source
+- flow style sequences ( \'a: b, c, d\') (based on request and test by
+ Anthony Sottile)
+- anchor names that are hand-crafted (i.e. not of the form`idNNN`)
+- [merges](http://yaml.org/type/merge.html) in dictionaries are
+ preserved
+
+This preservation is normally not broken unless you severely alter the
+structure of a component (delete a key in a dict, remove list entries).
+Reassigning values or replacing list items, etc., is fine.
+
+For the specific 1.2 differences see
+`yaml-1-2-support`{.interpreted-text role="ref"}
+
+Although individual indentation of lines is not preserved, you can
+specify separate indentation levels for mappings and sequences (counting
+for sequences does **not** include the dash for a sequence element) and
+specific offset of block sequence dashes within that indentation.
+
+Although `ruamel.yaml` still allows most of the PyYAML way of doing
+things, adding features required a different API then the transient
+nature of PyYAML\'s `Loader` and `Dumper`. Starting with `ruamel.yaml`
+version 0.15.0 this new API gets introduced. Old ways that get in the
+way will be removed, after first generating warnings on use, then
+generating an error. In general a warning in version 0.N.x will become
+an error in 0.N+1.0
+
+Many of the bugs filed against PyYAML, but that were never acted upon,
+have been fixed in `ruamel.yaml`
+--- !inc-raw |
+links.rydinc
diff --git a/_doc/pyyaml.ryd b/_doc/pyyaml.ryd
new file mode 100644
index 0000000..f670237
--- /dev/null
+++ b/_doc/pyyaml.ryd
@@ -0,0 +1,72 @@
+version: 0.2
+text: md
+pdf: false
+--- |
+# Differences with PyYAML
+
+::: parsed-literal
+
+*If I have seen further, it is by standing on the shoulders of giants*.
+
+: Isaac Newton (1676)
+:::
+
+`ruamel.yaml` is a derivative of Kirill Simonov\'s [PyYAML
+3.11](https://bitbucket.org/xi/pyyaml) and would not exist without that
+excellent base to start from.
+
+The following a summary of the major differences with PyYAML 3.11
+
+## Defaulting to YAML 1.2 support
+
+PyYAML supports the [YAML 1.1]() standard, `ruamel.yaml` supports [YAML
+1.2]() as released in 2009.
+
+- YAML 1.2 dropped support for several features unquoted `Yes`, `No`,
+ `On`, `Off`
+- YAML 1.2 no longer accepts strings that start with a `0` and solely
+ consist of number characters as octal, you need to specify such
+ strings with `0o[0-7]+` (zero + lower-case o for octal + one or more
+ octal characters).
+- YAML 1.2 no longer supports
+ [sexagesimals](https://en.wikipedia.org/wiki/Sexagesimal), so the
+ string scalar `12:34:56` doesn\'t need quoting.
+- `\/` escape for JSON compatibility
+- correct parsing of floating point scalars with exponentials
+
+unless the YAML document is loaded with an explicit `version==1.1` or
+the document starts with:
+
+ % YAML 1.1
+
+, `ruamel.yaml` will load the document as version 1.2.
+
+## PY2/PY3 reintegration
+
+`ruamel.yaml` re-integrates the Python 2 and 3 sources, running on
+Python 2.7 (CPython, PyPy), 3.3, 3.4, 3.5 and 3.6 (support for 2.6 has
+been dropped mid 2016). It is more easy to extend and maintain as only a
+miniscule part of the code is Python version specific.
+
+## Fixes
+
+- `ruamel.yaml` follows the `indent` keyword argument on scalars when
+ dumping.
+- `ruamel.yaml` allows `:` in plain scalars, as long as these are not
+ followed by a space (as per the specification)
+
+## Testing
+
+`ruamel.yaml` is tested using [tox]() and [py.test](). In addition to
+new tests, the original PyYAML test framework is called from within
+`tox` runs.
+
+Before versions are pushed to PyPI, `tox` is invoked, and has to pass,
+on all supported Python versions, on PyPI as well as flake8/pep8
+
+## API
+
+Starting with 0.15 the API for using `ruamel.yaml` has diverged allowing
+easier addition of new features.
+--- !inc-raw
+- links.rydinc
diff --git a/_test/data/a-nasty-libyaml-bug.loader-error b/_test/data/a-nasty-libyaml-bug.loader-error
new file mode 100644
index 0000000..f97d49f
--- /dev/null
+++ b/_test/data/a-nasty-libyaml-bug.loader-error
@@ -0,0 +1 @@
+[ [ \ No newline at end of file
diff --git a/_test/data/aliases-cdumper-bug.code b/_test/data/aliases-cdumper-bug.code
new file mode 100644
index 0000000..0168441
--- /dev/null
+++ b/_test/data/aliases-cdumper-bug.code
@@ -0,0 +1 @@
+[ today, today ]
diff --git a/_test/data/aliases.events b/_test/data/aliases.events
new file mode 100644
index 0000000..9139b51
--- /dev/null
+++ b/_test/data/aliases.events
@@ -0,0 +1,8 @@
+- !StreamStart
+- !DocumentStart
+- !SequenceStart
+- !Scalar { anchor: 'myanchor', tag: '!mytag', value: 'data' }
+- !Alias { anchor: 'myanchor' }
+- !SequenceEnd
+- !DocumentEnd
+- !StreamEnd
diff --git a/_test/data/bool.data b/_test/data/bool.data
new file mode 100644
index 0000000..ff99e77
--- /dev/null
+++ b/_test/data/bool.data
@@ -0,0 +1,18 @@
+- yes
+- Yes
+- YES
+- no
+- No
+- NO
+- true
+- True
+- TRUE
+- false
+- False
+- FALSE
+- on
+- On
+- ON
+- off
+- Off
+- OFF
diff --git a/_test/data/bool.detect b/_test/data/bool.detect
new file mode 100644
index 0000000..947ebbb
--- /dev/null
+++ b/_test/data/bool.detect
@@ -0,0 +1 @@
+tag:yaml.org,2002:bool
diff --git a/_test/data/colon-in-flow-context.loader-error b/_test/data/colon-in-flow-context.loader-error
new file mode 100644
index 0000000..13d5087
--- /dev/null
+++ b/_test/data/colon-in-flow-context.loader-error
@@ -0,0 +1 @@
+{ foo:bar }
diff --git a/_test/data/comment_no_eol.data b/_test/data/comment_no_eol.data
new file mode 100644
index 0000000..f7b15f6
--- /dev/null
+++ b/_test/data/comment_no_eol.data
@@ -0,0 +1 @@
+european: 10 # abc \ No newline at end of file
diff --git a/_test/data/composite_key.code b/_test/data/composite_key.code
new file mode 100644
index 0000000..627b049
--- /dev/null
+++ b/_test/data/composite_key.code
@@ -0,0 +1 @@
+{('foo', 'bar'): 'baz'}
diff --git a/_test/data/composite_key.data b/_test/data/composite_key.data
new file mode 100644
index 0000000..d748e37
--- /dev/null
+++ b/_test/data/composite_key.data
@@ -0,0 +1,4 @@
+---
+? - foo
+ - bar
+: baz
diff --git a/_test/data/construct-binary-py2.code b/_test/data/construct-binary-py2.code
new file mode 100644
index 0000000..67ac0d5
--- /dev/null
+++ b/_test/data/construct-binary-py2.code
@@ -0,0 +1,7 @@
+{
+ "canonical":
+ "GIF89a\x0c\x00\x0c\x00\x84\x00\x00\xff\xff\xf7\xf5\xf5\xee\xe9\xe9\xe5fff\x00\x00\x00\xe7\xe7\xe7^^^\xf3\xf3\xed\x8e\x8e\x8e\xe0\xe0\xe0\x9f\x9f\x9f\x93\x93\x93\xa7\xa7\xa7\x9e\x9e\x9eiiiccc\xa3\xa3\xa3\x84\x84\x84\xff\xfe\xf9\xff\xfe\xf9\xff\xfe\xf9\xff\xfe\xf9\xff\xfe\xf9\xff\xfe\xf9\xff\xfe\xf9\xff\xfe\xf9\xff\xfe\xf9\xff\xfe\xf9\xff\xfe\xf9\xff\xfe\xf9\xff\xfe\xf9\xff\xfe\xf9!\xfe\x0eMade with GIMP\x00,\x00\x00\x00\x00\x0c\x00\x0c\x00\x00\x05, \x8e\x810\x9e\xe3@\x14\xe8i\x10\xc4\xd1\x8a\x08\x1c\xcf\x80M$z\xef\xff0\x85p\xb8\xb01f\r\x1b\xce\x01\xc3\x01\x1e\x10' \x82\n\x01\x00;",
+ "generic":
+ "GIF89a\x0c\x00\x0c\x00\x84\x00\x00\xff\xff\xf7\xf5\xf5\xee\xe9\xe9\xe5fff\x00\x00\x00\xe7\xe7\xe7^^^\xf3\xf3\xed\x8e\x8e\x8e\xe0\xe0\xe0\x9f\x9f\x9f\x93\x93\x93\xa7\xa7\xa7\x9e\x9e\x9eiiiccc\xa3\xa3\xa3\x84\x84\x84\xff\xfe\xf9\xff\xfe\xf9\xff\xfe\xf9\xff\xfe\xf9\xff\xfe\xf9\xff\xfe\xf9\xff\xfe\xf9\xff\xfe\xf9\xff\xfe\xf9\xff\xfe\xf9\xff\xfe\xf9\xff\xfe\xf9\xff\xfe\xf9\xff\xfe\xf9!\xfe\x0eMade with GIMP\x00,\x00\x00\x00\x00\x0c\x00\x0c\x00\x00\x05, \x8e\x810\x9e\xe3@\x14\xe8i\x10\xc4\xd1\x8a\x08\x1c\xcf\x80M$z\xef\xff0\x85p\xb8\xb01f\r\x1b\xce\x01\xc3\x01\x1e\x10' \x82\n\x01\x00;",
+ "description": "The binary value above is a tiny arrow encoded as a gif image.",
+}
diff --git a/_test/data/construct-binary-py2.data b/_test/data/construct-binary-py2.data
new file mode 100644
index 0000000..dcdb16f
--- /dev/null
+++ b/_test/data/construct-binary-py2.data
@@ -0,0 +1,12 @@
+canonical: !!binary "\
+ R0lGODlhDAAMAIQAAP//9/X17unp5WZmZgAAAOfn515eXvPz7Y6OjuDg4J+fn5\
+ OTk6enp56enmlpaWNjY6Ojo4SEhP/++f/++f/++f/++f/++f/++f/++f/++f/+\
+ +f/++f/++f/++f/++f/++SH+Dk1hZGUgd2l0aCBHSU1QACwAAAAADAAMAAAFLC\
+ AgjoEwnuNAFOhpEMTRiggcz4BNJHrv/zCFcLiwMWYNG84BwwEeECcgggoBADs="
+generic: !!binary |
+ R0lGODlhDAAMAIQAAP//9/X17unp5WZmZgAAAOfn515eXvPz7Y6OjuDg4J+fn5
+ OTk6enp56enmlpaWNjY6Ojo4SEhP/++f/++f/++f/++f/++f/++f/++f/++f/+
+ +f/++f/++f/++f/++f/++SH+Dk1hZGUgd2l0aCBHSU1QACwAAAAADAAMAAAFLC
+ AgjoEwnuNAFOhpEMTRiggcz4BNJHrv/zCFcLiwMWYNG84BwwEeECcgggoBADs=
+description:
+ The binary value above is a tiny arrow encoded as a gif image.
diff --git a/_test/data/construct-binary-py3.code b/_test/data/construct-binary-py3.code
new file mode 100644
index 0000000..30bfc3f
--- /dev/null
+++ b/_test/data/construct-binary-py3.code
@@ -0,0 +1,7 @@
+{
+ "canonical":
+ b"GIF89a\x0c\x00\x0c\x00\x84\x00\x00\xff\xff\xf7\xf5\xf5\xee\xe9\xe9\xe5fff\x00\x00\x00\xe7\xe7\xe7^^^\xf3\xf3\xed\x8e\x8e\x8e\xe0\xe0\xe0\x9f\x9f\x9f\x93\x93\x93\xa7\xa7\xa7\x9e\x9e\x9eiiiccc\xa3\xa3\xa3\x84\x84\x84\xff\xfe\xf9\xff\xfe\xf9\xff\xfe\xf9\xff\xfe\xf9\xff\xfe\xf9\xff\xfe\xf9\xff\xfe\xf9\xff\xfe\xf9\xff\xfe\xf9\xff\xfe\xf9\xff\xfe\xf9\xff\xfe\xf9\xff\xfe\xf9\xff\xfe\xf9!\xfe\x0eMade with GIMP\x00,\x00\x00\x00\x00\x0c\x00\x0c\x00\x00\x05, \x8e\x810\x9e\xe3@\x14\xe8i\x10\xc4\xd1\x8a\x08\x1c\xcf\x80M$z\xef\xff0\x85p\xb8\xb01f\r\x1b\xce\x01\xc3\x01\x1e\x10' \x82\n\x01\x00;",
+ "generic":
+ b"GIF89a\x0c\x00\x0c\x00\x84\x00\x00\xff\xff\xf7\xf5\xf5\xee\xe9\xe9\xe5fff\x00\x00\x00\xe7\xe7\xe7^^^\xf3\xf3\xed\x8e\x8e\x8e\xe0\xe0\xe0\x9f\x9f\x9f\x93\x93\x93\xa7\xa7\xa7\x9e\x9e\x9eiiiccc\xa3\xa3\xa3\x84\x84\x84\xff\xfe\xf9\xff\xfe\xf9\xff\xfe\xf9\xff\xfe\xf9\xff\xfe\xf9\xff\xfe\xf9\xff\xfe\xf9\xff\xfe\xf9\xff\xfe\xf9\xff\xfe\xf9\xff\xfe\xf9\xff\xfe\xf9\xff\xfe\xf9\xff\xfe\xf9!\xfe\x0eMade with GIMP\x00,\x00\x00\x00\x00\x0c\x00\x0c\x00\x00\x05, \x8e\x810\x9e\xe3@\x14\xe8i\x10\xc4\xd1\x8a\x08\x1c\xcf\x80M$z\xef\xff0\x85p\xb8\xb01f\r\x1b\xce\x01\xc3\x01\x1e\x10' \x82\n\x01\x00;",
+ "description": "The binary value above is a tiny arrow encoded as a gif image.",
+}
diff --git a/_test/data/construct-binary-py3.data b/_test/data/construct-binary-py3.data
new file mode 100644
index 0000000..dcdb16f
--- /dev/null
+++ b/_test/data/construct-binary-py3.data
@@ -0,0 +1,12 @@
+canonical: !!binary "\
+ R0lGODlhDAAMAIQAAP//9/X17unp5WZmZgAAAOfn515eXvPz7Y6OjuDg4J+fn5\
+ OTk6enp56enmlpaWNjY6Ojo4SEhP/++f/++f/++f/++f/++f/++f/++f/++f/+\
+ +f/++f/++f/++f/++f/++SH+Dk1hZGUgd2l0aCBHSU1QACwAAAAADAAMAAAFLC\
+ AgjoEwnuNAFOhpEMTRiggcz4BNJHrv/zCFcLiwMWYNG84BwwEeECcgggoBADs="
+generic: !!binary |
+ R0lGODlhDAAMAIQAAP//9/X17unp5WZmZgAAAOfn515eXvPz7Y6OjuDg4J+fn5
+ OTk6enp56enmlpaWNjY6Ojo4SEhP/++f/++f/++f/++f/++f/++f/++f/++f/+
+ +f/++f/++f/++f/++f/++SH+Dk1hZGUgd2l0aCBHSU1QACwAAAAADAAMAAAFLC
+ AgjoEwnuNAFOhpEMTRiggcz4BNJHrv/zCFcLiwMWYNG84BwwEeECcgggoBADs=
+description:
+ The binary value above is a tiny arrow encoded as a gif image.
diff --git a/_test/data/construct-bool.code b/_test/data/construct-bool.code
new file mode 100644
index 0000000..3d02580
--- /dev/null
+++ b/_test/data/construct-bool.code
@@ -0,0 +1,7 @@
+{
+ "canonical": True,
+ "answer": False,
+ "logical": True,
+ "option": True,
+ "but": { "y": "is a string", "n": "is a string" },
+}
diff --git a/_test/data/construct-bool.data b/_test/data/construct-bool.data
new file mode 100644
index 0000000..36d6519
--- /dev/null
+++ b/_test/data/construct-bool.data
@@ -0,0 +1,9 @@
+canonical: yes
+answer: NO
+logical: True
+option: on
+
+
+but:
+ y: is a string
+ n: is a string
diff --git a/_test/data/construct-custom.code b/_test/data/construct-custom.code
new file mode 100644
index 0000000..2d5f063
--- /dev/null
+++ b/_test/data/construct-custom.code
@@ -0,0 +1,10 @@
+[
+ MyTestClass1(x=1),
+ MyTestClass1(x=1, y=2, z=3),
+ MyTestClass2(x=10),
+ MyTestClass2(x=10, y=20, z=30),
+ MyTestClass3(x=1),
+ MyTestClass3(x=1, y=2, z=3),
+ MyTestClass3(x=1, y=2, z=3),
+ YAMLObject1(my_parameter='foo', my_another_parameter=[1,2,3])
+]
diff --git a/_test/data/construct-custom.data b/_test/data/construct-custom.data
new file mode 100644
index 0000000..9db0f64
--- /dev/null
+++ b/_test/data/construct-custom.data
@@ -0,0 +1,26 @@
+---
+- !tag1
+ x: 1
+- !tag1
+ x: 1
+ 'y': 2
+ z: 3
+- !tag2
+ 10
+- !tag2
+ =: 10
+ 'y': 20
+ z: 30
+- !tag3
+ x: 1
+- !tag3
+ x: 1
+ 'y': 2
+ z: 3
+- !tag3
+ =: 1
+ 'y': 2
+ z: 3
+- !foo
+ my-parameter: foo
+ my-another-parameter: [1,2,3]
diff --git a/_test/data/construct-float.code b/_test/data/construct-float.code
new file mode 100644
index 0000000..8493bf2
--- /dev/null
+++ b/_test/data/construct-float.code
@@ -0,0 +1,8 @@
+{
+ "canonical": 685230.15,
+ "exponential": 685230.15,
+ "fixed": 685230.15,
+ "sexagesimal": 685230.15,
+ "negative infinity": -1e300000,
+ "not a number": 1e300000/1e300000,
+}
diff --git a/_test/data/construct-float.data b/_test/data/construct-float.data
new file mode 100644
index 0000000..b662c62
--- /dev/null
+++ b/_test/data/construct-float.data
@@ -0,0 +1,6 @@
+canonical: 6.8523015e+5
+exponential: 685.230_15e+03
+fixed: 685_230.15
+sexagesimal: 190:20:30.15
+negative infinity: -.inf
+not a number: .NaN
diff --git a/_test/data/construct-int.code b/_test/data/construct-int.code
new file mode 100644
index 0000000..1058f7b
--- /dev/null
+++ b/_test/data/construct-int.code
@@ -0,0 +1,8 @@
+{
+ "canonical": 685230,
+ "decimal": 685230,
+ "octal": 685230,
+ "hexadecimal": 685230,
+ "binary": 685230,
+ "sexagesimal": 685230,
+}
diff --git a/_test/data/construct-int.data b/_test/data/construct-int.data
new file mode 100644
index 0000000..852c314
--- /dev/null
+++ b/_test/data/construct-int.data
@@ -0,0 +1,6 @@
+canonical: 685230
+decimal: +685_230
+octal: 02472256
+hexadecimal: 0x_0A_74_AE
+binary: 0b1010_0111_0100_1010_1110
+sexagesimal: 190:20:30
diff --git a/_test/data/construct-map.code b/_test/data/construct-map.code
new file mode 100644
index 0000000..736ba48
--- /dev/null
+++ b/_test/data/construct-map.code
@@ -0,0 +1,6 @@
+{
+ "Block style":
+ { "Clark" : "Evans", "Brian" : "Ingerson", "Oren" : "Ben-Kiki" },
+ "Flow style":
+ { "Clark" : "Evans", "Brian" : "Ingerson", "Oren" : "Ben-Kiki" },
+}
diff --git a/_test/data/construct-map.data b/_test/data/construct-map.data
new file mode 100644
index 0000000..022446d
--- /dev/null
+++ b/_test/data/construct-map.data
@@ -0,0 +1,6 @@
+# Unordered set of key: value pairs.
+Block style: !!map
+ Clark : Evans
+ Brian : Ingerson
+ Oren : Ben-Kiki
+Flow style: !!map { Clark: Evans, Brian: Ingerson, Oren: Ben-Kiki }
diff --git a/_test/data/construct-merge.code b/_test/data/construct-merge.code
new file mode 100644
index 0000000..6cd419d
--- /dev/null
+++ b/_test/data/construct-merge.code
@@ -0,0 +1,10 @@
+[
+ { "x": 1, "y": 2 },
+ { "x": 0, "y": 2 },
+ { "r": 10 },
+ { "r": 1 },
+ { "x": 1, "y": 2, "r": 10, "label": "center/big" },
+ { "x": 1, "y": 2, "r": 10, "label": "center/big" },
+ { "x": 1, "y": 2, "r": 10, "label": "center/big" },
+ { "x": 1, "y": 2, "r": 10, "label": "center/big" },
+]
diff --git a/_test/data/construct-merge.data b/_test/data/construct-merge.data
new file mode 100644
index 0000000..3fdb2e2
--- /dev/null
+++ b/_test/data/construct-merge.data
@@ -0,0 +1,27 @@
+---
+- &CENTER { x: 1, 'y': 2 }
+- &LEFT { x: 0, 'y': 2 }
+- &BIG { r: 10 }
+- &SMALL { r: 1 }
+
+# All the following maps are equal:
+
+- # Explicit keys
+ x: 1
+ 'y': 2
+ r: 10
+ label: center/big
+
+- # Merge one map
+ << : *CENTER
+ r: 10
+ label: center/big
+
+- # Merge multiple maps
+ << : [ *CENTER, *BIG ]
+ label: center/big
+
+- # Override
+ << : [ *BIG, *LEFT, *SMALL ]
+ x: 1
+ label: center/big
diff --git a/_test/data/construct-null.code b/_test/data/construct-null.code
new file mode 100644
index 0000000..a895eaa
--- /dev/null
+++ b/_test/data/construct-null.code
@@ -0,0 +1,13 @@
+[
+ None,
+ { "empty": None, "canonical": None, "english": None, None: "null key" },
+ {
+ "sparse": [
+ None,
+ "2nd entry",
+ None,
+ "4th entry",
+ None,
+ ],
+ },
+]
diff --git a/_test/data/construct-null.data b/_test/data/construct-null.data
new file mode 100644
index 0000000..9ad0344
--- /dev/null
+++ b/_test/data/construct-null.data
@@ -0,0 +1,18 @@
+# A document may be null.
+---
+---
+# This mapping has four keys,
+# one has a value.
+empty:
+canonical: ~
+english: null
+~: null key
+---
+# This sequence has five
+# entries, two have values.
+sparse:
+ - ~
+ - 2nd entry
+ -
+ - 4th entry
+ - Null
diff --git a/_test/data/construct-omap.code b/_test/data/construct-omap.code
new file mode 100644
index 0000000..33a1574
--- /dev/null
+++ b/_test/data/construct-omap.code
@@ -0,0 +1,8 @@
+{
+ "Bestiary": ordereddict([
+ ("aardvark", "African pig-like ant eater. Ugly."),
+ ("anteater", "South-American ant eater. Two species."),
+ ("anaconda", "South-American constrictor snake. Scaly."),
+ ]),
+ "Numbers": ordereddict([ ("one", 4), ("one", 1), ("two", 2), ("three", 3) ]),
+}
diff --git a/_test/data/construct-omap.data b/_test/data/construct-omap.data
new file mode 100644
index 0000000..4fa0f45
--- /dev/null
+++ b/_test/data/construct-omap.data
@@ -0,0 +1,8 @@
+# Explicitly typed ordered map (dictionary).
+Bestiary: !!omap
+ - aardvark: African pig-like ant eater. Ugly.
+ - anteater: South-American ant eater. Two species.
+ - anaconda: South-American constrictor snake. Scaly.
+ # Etc.
+# Flow style
+Numbers: !!omap [ one: 1, two: 2, three : 3 ]
diff --git a/_test/data/construct-pairs.code b/_test/data/construct-pairs.code
new file mode 100644
index 0000000..64f86ee
--- /dev/null
+++ b/_test/data/construct-pairs.code
@@ -0,0 +1,9 @@
+{
+ "Block tasks": [
+ ("meeting", "with team."),
+ ("meeting", "with boss."),
+ ("break", "lunch."),
+ ("meeting", "with client."),
+ ],
+ "Flow tasks": [ ("meeting", "with team"), ("meeting", "with boss") ],
+}
diff --git a/_test/data/construct-pairs.data b/_test/data/construct-pairs.data
new file mode 100644
index 0000000..05f55b9
--- /dev/null
+++ b/_test/data/construct-pairs.data
@@ -0,0 +1,7 @@
+# Explicitly typed pairs.
+Block tasks: !!pairs
+ - meeting: with team.
+ - meeting: with boss.
+ - break: lunch.
+ - meeting: with client.
+Flow tasks: !!pairs [ meeting: with team, meeting: with boss ]
diff --git a/_test/data/construct-python-bool.code b/_test/data/construct-python-bool.code
new file mode 100644
index 0000000..170da01
--- /dev/null
+++ b/_test/data/construct-python-bool.code
@@ -0,0 +1 @@
+[ True, False ]
diff --git a/_test/data/construct-python-bool.data b/_test/data/construct-python-bool.data
new file mode 100644
index 0000000..0068869
--- /dev/null
+++ b/_test/data/construct-python-bool.data
@@ -0,0 +1 @@
+[ !!python/bool True, !!python/bool False ]
diff --git a/_test/data/construct-python-bytes-py3.code b/_test/data/construct-python-bytes-py3.code
new file mode 100644
index 0000000..b9051d8
--- /dev/null
+++ b/_test/data/construct-python-bytes-py3.code
@@ -0,0 +1 @@
+b'some binary data'
diff --git a/_test/data/construct-python-bytes-py3.data b/_test/data/construct-python-bytes-py3.data
new file mode 100644
index 0000000..9528725
--- /dev/null
+++ b/_test/data/construct-python-bytes-py3.data
@@ -0,0 +1 @@
+--- !!python/bytes 'c29tZSBiaW5hcnkgZGF0YQ=='
diff --git a/_test/data/construct-python-complex.code b/_test/data/construct-python-complex.code
new file mode 100644
index 0000000..e582dff
--- /dev/null
+++ b/_test/data/construct-python-complex.code
@@ -0,0 +1 @@
+[0.5+0j, 0.5+0.5j, 0.5j, -0.5+0.5j, -0.5+0j, -0.5-0.5j, -0.5j, 0.5-0.5j]
diff --git a/_test/data/construct-python-complex.data b/_test/data/construct-python-complex.data
new file mode 100644
index 0000000..17ebad4
--- /dev/null
+++ b/_test/data/construct-python-complex.data
@@ -0,0 +1,8 @@
+- !!python/complex 0.5+0j
+- !!python/complex 0.5+0.5j
+- !!python/complex 0.5j
+- !!python/complex -0.5+0.5j
+- !!python/complex -0.5+0j
+- !!python/complex -0.5-0.5j
+- !!python/complex -0.5j
+- !!python/complex 0.5-0.5j
diff --git a/_test/data/construct-python-float.code b/_test/data/construct-python-float.code
new file mode 100644
index 0000000..d5910a0
--- /dev/null
+++ b/_test/data/construct-python-float.code
@@ -0,0 +1 @@
+123.456
diff --git a/_test/data/construct-python-float.data b/_test/data/construct-python-float.data
new file mode 100644
index 0000000..b460eb8
--- /dev/null
+++ b/_test/data/construct-python-float.data
@@ -0,0 +1 @@
+!!python/float 123.456
diff --git a/_test/data/construct-python-int.code b/_test/data/construct-python-int.code
new file mode 100644
index 0000000..190a180
--- /dev/null
+++ b/_test/data/construct-python-int.code
@@ -0,0 +1 @@
+123
diff --git a/_test/data/construct-python-int.data b/_test/data/construct-python-int.data
new file mode 100644
index 0000000..741d669
--- /dev/null
+++ b/_test/data/construct-python-int.data
@@ -0,0 +1 @@
+!!python/int 123
diff --git a/_test/data/construct-python-long-short-py2.code b/_test/data/construct-python-long-short-py2.code
new file mode 100644
index 0000000..fafc3f1
--- /dev/null
+++ b/_test/data/construct-python-long-short-py2.code
@@ -0,0 +1 @@
+123L
diff --git a/_test/data/construct-python-long-short-py2.data b/_test/data/construct-python-long-short-py2.data
new file mode 100644
index 0000000..4bd5dc2
--- /dev/null
+++ b/_test/data/construct-python-long-short-py2.data
@@ -0,0 +1 @@
+!!python/long 123
diff --git a/_test/data/construct-python-long-short-py3.code b/_test/data/construct-python-long-short-py3.code
new file mode 100644
index 0000000..190a180
--- /dev/null
+++ b/_test/data/construct-python-long-short-py3.code
@@ -0,0 +1 @@
+123
diff --git a/_test/data/construct-python-long-short-py3.data b/_test/data/construct-python-long-short-py3.data
new file mode 100644
index 0000000..4bd5dc2
--- /dev/null
+++ b/_test/data/construct-python-long-short-py3.data
@@ -0,0 +1 @@
+!!python/long 123
diff --git a/_test/data/construct-python-name-module.code b/_test/data/construct-python-name-module.code
new file mode 100644
index 0000000..6f39148
--- /dev/null
+++ b/_test/data/construct-python-name-module.code
@@ -0,0 +1 @@
+[str, yaml.Loader, yaml.dump, abs, yaml.tokens]
diff --git a/_test/data/construct-python-name-module.data b/_test/data/construct-python-name-module.data
new file mode 100644
index 0000000..f0c9712
--- /dev/null
+++ b/_test/data/construct-python-name-module.data
@@ -0,0 +1,5 @@
+- !!python/name:str
+- !!python/name:yaml.Loader
+- !!python/name:yaml.dump
+- !!python/name:abs
+- !!python/module:yaml.tokens
diff --git a/_test/data/construct-python-none.code b/_test/data/construct-python-none.code
new file mode 100644
index 0000000..b0047fa
--- /dev/null
+++ b/_test/data/construct-python-none.code
@@ -0,0 +1 @@
+None
diff --git a/_test/data/construct-python-none.data b/_test/data/construct-python-none.data
new file mode 100644
index 0000000..7907ec3
--- /dev/null
+++ b/_test/data/construct-python-none.data
@@ -0,0 +1 @@
+!!python/none
diff --git a/_test/data/construct-python-object.code b/_test/data/construct-python-object.code
new file mode 100644
index 0000000..7f1edf1
--- /dev/null
+++ b/_test/data/construct-python-object.code
@@ -0,0 +1,23 @@
+[
+AnObject(1, 'two', [3,3,3]),
+AnInstance(1, 'two', [3,3,3]),
+
+AnObject(1, 'two', [3,3,3]),
+AnInstance(1, 'two', [3,3,3]),
+
+AState(1, 'two', [3,3,3]),
+ACustomState(1, 'two', [3,3,3]),
+
+InitArgs(1, 'two', [3,3,3]),
+InitArgsWithState(1, 'two', [3,3,3]),
+
+NewArgs(1, 'two', [3,3,3]),
+NewArgsWithState(1, 'two', [3,3,3]),
+
+Reduce(1, 'two', [3,3,3]),
+ReduceWithState(1, 'two', [3,3,3]),
+
+MyInt(3),
+MyList(3),
+MyDict(3),
+]
diff --git a/_test/data/construct-python-object.data b/_test/data/construct-python-object.data
new file mode 100644
index 0000000..bce8b2e
--- /dev/null
+++ b/_test/data/construct-python-object.data
@@ -0,0 +1,21 @@
+- !!python/object:test_constructor.AnObject { foo: 1, bar: two, baz: [3,3,3] }
+- !!python/object:test_constructor.AnInstance { foo: 1, bar: two, baz: [3,3,3] }
+
+- !!python/object/new:test_constructor.AnObject { args: [1, two], kwds: {baz: [3,3,3]} }
+- !!python/object/apply:test_constructor.AnInstance { args: [1, two], kwds: {baz: [3,3,3]} }
+
+- !!python/object:test_constructor.AState { _foo: 1, _bar: two, _baz: [3,3,3] }
+- !!python/object/new:test_constructor.ACustomState { state: !!python/tuple [1, two, [3,3,3]] }
+
+- !!python/object/new:test_constructor.InitArgs [1, two, [3,3,3]]
+- !!python/object/new:test_constructor.InitArgsWithState { args: [1, two], state: [3,3,3] }
+
+- !!python/object/new:test_constructor.NewArgs [1, two, [3,3,3]]
+- !!python/object/new:test_constructor.NewArgsWithState { args: [1, two], state: [3,3,3] }
+
+- !!python/object/apply:test_constructor.Reduce [1, two, [3,3,3]]
+- !!python/object/apply:test_constructor.ReduceWithState { args: [1, two], state: [3,3,3] }
+
+- !!python/object/new:test_constructor.MyInt [3]
+- !!python/object/new:test_constructor.MyList { listitems: [~, ~, ~] }
+- !!python/object/new:test_constructor.MyDict { dictitems: {0, 1, 2} }
diff --git a/_test/data/construct-python-str-ascii.code b/_test/data/construct-python-str-ascii.code
new file mode 100644
index 0000000..d9d62f6
--- /dev/null
+++ b/_test/data/construct-python-str-ascii.code
@@ -0,0 +1 @@
+"ascii string"
diff --git a/_test/data/construct-python-str-ascii.data b/_test/data/construct-python-str-ascii.data
new file mode 100644
index 0000000..a83349e
--- /dev/null
+++ b/_test/data/construct-python-str-ascii.data
@@ -0,0 +1 @@
+--- !!python/str "ascii string"
diff --git a/_test/data/construct-python-str-utf8-py2.code b/_test/data/construct-python-str-utf8-py2.code
new file mode 100644
index 0000000..6ca7d8f
--- /dev/null
+++ b/_test/data/construct-python-str-utf8-py2.code
@@ -0,0 +1 @@
+'\u042d\u0442\u043e \u0443\u043d\u0438\u043a\u043e\u0434\u043d\u0430\u044f \u0441\u0442\u0440\u043e\u043a\u0430'.encode('utf-8')
diff --git a/_test/data/construct-python-str-utf8-py2.data b/_test/data/construct-python-str-utf8-py2.data
new file mode 100644
index 0000000..9ef2c72
--- /dev/null
+++ b/_test/data/construct-python-str-utf8-py2.data
@@ -0,0 +1 @@
+--- !!python/str "Это уникодная строка"
diff --git a/_test/data/construct-python-str-utf8-py3.code b/_test/data/construct-python-str-utf8-py3.code
new file mode 100644
index 0000000..9f66032
--- /dev/null
+++ b/_test/data/construct-python-str-utf8-py3.code
@@ -0,0 +1 @@
+'\u042d\u0442\u043e \u0443\u043d\u0438\u043a\u043e\u0434\u043d\u0430\u044f \u0441\u0442\u0440\u043e\u043a\u0430'
diff --git a/_test/data/construct-python-str-utf8-py3.data b/_test/data/construct-python-str-utf8-py3.data
new file mode 100644
index 0000000..9ef2c72
--- /dev/null
+++ b/_test/data/construct-python-str-utf8-py3.data
@@ -0,0 +1 @@
+--- !!python/str "Это уникодная строка"
diff --git a/_test/data/construct-python-tuple-list-dict.code b/_test/data/construct-python-tuple-list-dict.code
new file mode 100644
index 0000000..20ced98
--- /dev/null
+++ b/_test/data/construct-python-tuple-list-dict.code
@@ -0,0 +1,6 @@
+[
+ [1, 2, 3, 4],
+ (1, 2, 3, 4),
+ {1: 2, 3: 4},
+ {(0,0): 0, (0,1): 1, (1,0): 1, (1,1): 0},
+]
diff --git a/_test/data/construct-python-tuple-list-dict.data b/_test/data/construct-python-tuple-list-dict.data
new file mode 100644
index 0000000..c56159b
--- /dev/null
+++ b/_test/data/construct-python-tuple-list-dict.data
@@ -0,0 +1,8 @@
+- !!python/list [1, 2, 3, 4]
+- !!python/tuple [1, 2, 3, 4]
+- !!python/dict {1: 2, 3: 4}
+- !!python/dict
+ !!python/tuple [0,0]: 0
+ !!python/tuple [0,1]: 1
+ !!python/tuple [1,0]: 1
+ !!python/tuple [1,1]: 0
diff --git a/_test/data/construct-python-unicode-ascii-py2.code b/_test/data/construct-python-unicode-ascii-py2.code
new file mode 100644
index 0000000..d4cd82c
--- /dev/null
+++ b/_test/data/construct-python-unicode-ascii-py2.code
@@ -0,0 +1 @@
+u"ascii string"
diff --git a/_test/data/construct-python-unicode-ascii-py2.data b/_test/data/construct-python-unicode-ascii-py2.data
new file mode 100644
index 0000000..3a0647b
--- /dev/null
+++ b/_test/data/construct-python-unicode-ascii-py2.data
@@ -0,0 +1 @@
+--- !!python/unicode "ascii string"
diff --git a/_test/data/construct-python-unicode-ascii-py3.code b/_test/data/construct-python-unicode-ascii-py3.code
new file mode 100644
index 0000000..d9d62f6
--- /dev/null
+++ b/_test/data/construct-python-unicode-ascii-py3.code
@@ -0,0 +1 @@
+"ascii string"
diff --git a/_test/data/construct-python-unicode-ascii-py3.data b/_test/data/construct-python-unicode-ascii-py3.data
new file mode 100644
index 0000000..3a0647b
--- /dev/null
+++ b/_test/data/construct-python-unicode-ascii-py3.data
@@ -0,0 +1 @@
+--- !!python/unicode "ascii string"
diff --git a/_test/data/construct-python-unicode-utf8-py2.code b/_test/data/construct-python-unicode-utf8-py2.code
new file mode 100644
index 0000000..9f66032
--- /dev/null
+++ b/_test/data/construct-python-unicode-utf8-py2.code
@@ -0,0 +1 @@
+'\u042d\u0442\u043e \u0443\u043d\u0438\u043a\u043e\u0434\u043d\u0430\u044f \u0441\u0442\u0440\u043e\u043a\u0430'
diff --git a/_test/data/construct-python-unicode-utf8-py2.data b/_test/data/construct-python-unicode-utf8-py2.data
new file mode 100644
index 0000000..5a980ea
--- /dev/null
+++ b/_test/data/construct-python-unicode-utf8-py2.data
@@ -0,0 +1 @@
+--- !!python/unicode "Это уникодная строка"
diff --git a/_test/data/construct-python-unicode-utf8-py3.code b/_test/data/construct-python-unicode-utf8-py3.code
new file mode 100644
index 0000000..9f66032
--- /dev/null
+++ b/_test/data/construct-python-unicode-utf8-py3.code
@@ -0,0 +1 @@
+'\u042d\u0442\u043e \u0443\u043d\u0438\u043a\u043e\u0434\u043d\u0430\u044f \u0441\u0442\u0440\u043e\u043a\u0430'
diff --git a/_test/data/construct-python-unicode-utf8-py3.data b/_test/data/construct-python-unicode-utf8-py3.data
new file mode 100644
index 0000000..5a980ea
--- /dev/null
+++ b/_test/data/construct-python-unicode-utf8-py3.data
@@ -0,0 +1 @@
+--- !!python/unicode "Это уникодная строка"
diff --git a/_test/data/construct-seq.code b/_test/data/construct-seq.code
new file mode 100644
index 0000000..0c90c05
--- /dev/null
+++ b/_test/data/construct-seq.code
@@ -0,0 +1,4 @@
+{
+ "Block style": ["Mercury", "Venus", "Earth", "Mars", "Jupiter", "Saturn", "Uranus", "Neptune", "Pluto"],
+ "Flow style": ["Mercury", "Venus", "Earth", "Mars", "Jupiter", "Saturn", "Uranus", "Neptune", "Pluto"],
+}
diff --git a/_test/data/construct-seq.data b/_test/data/construct-seq.data
new file mode 100644
index 0000000..bb92fd1
--- /dev/null
+++ b/_test/data/construct-seq.data
@@ -0,0 +1,15 @@
+# Ordered sequence of nodes
+Block style: !!seq
+- Mercury # Rotates - no light/dark sides.
+- Venus # Deadliest. Aptly named.
+- Earth # Mostly dirt.
+- Mars # Seems empty.
+- Jupiter # The king.
+- Saturn # Pretty.
+- Uranus # Where the sun hardly shines.
+- Neptune # Boring. No rings.
+- Pluto # You call this a planet?
+Flow style: !!seq [ Mercury, Venus, Earth, Mars, # Rocks
+ Jupiter, Saturn, Uranus, Neptune, # Gas
+ Pluto ] # Overrated
+
diff --git a/_test/data/construct-set.code b/_test/data/construct-set.code
new file mode 100644
index 0000000..aa090e8
--- /dev/null
+++ b/_test/data/construct-set.code
@@ -0,0 +1,4 @@
+{
+ "baseball players": set(["Mark McGwire", "Sammy Sosa", "Ken Griffey"]),
+ "baseball teams": set(["Boston Red Sox", "Detroit Tigers", "New York Yankees"]),
+}
diff --git a/_test/data/construct-set.data b/_test/data/construct-set.data
new file mode 100644
index 0000000..e05dc88
--- /dev/null
+++ b/_test/data/construct-set.data
@@ -0,0 +1,7 @@
+# Explicitly typed set.
+baseball players: !!set
+ ? Mark McGwire
+ ? Sammy Sosa
+ ? Ken Griffey
+# Flow style
+baseball teams: !!set { Boston Red Sox, Detroit Tigers, New York Yankees }
diff --git a/_test/data/construct-str-ascii.code b/_test/data/construct-str-ascii.code
new file mode 100644
index 0000000..d9d62f6
--- /dev/null
+++ b/_test/data/construct-str-ascii.code
@@ -0,0 +1 @@
+"ascii string"
diff --git a/_test/data/construct-str-ascii.data b/_test/data/construct-str-ascii.data
new file mode 100644
index 0000000..0d93013
--- /dev/null
+++ b/_test/data/construct-str-ascii.data
@@ -0,0 +1 @@
+--- !!str "ascii string"
diff --git a/_test/data/construct-str-utf8-py2.code b/_test/data/construct-str-utf8-py2.code
new file mode 100644
index 0000000..9f66032
--- /dev/null
+++ b/_test/data/construct-str-utf8-py2.code
@@ -0,0 +1 @@
+'\u042d\u0442\u043e \u0443\u043d\u0438\u043a\u043e\u0434\u043d\u0430\u044f \u0441\u0442\u0440\u043e\u043a\u0430'
diff --git a/_test/data/construct-str-utf8-py2.data b/_test/data/construct-str-utf8-py2.data
new file mode 100644
index 0000000..e355f18
--- /dev/null
+++ b/_test/data/construct-str-utf8-py2.data
@@ -0,0 +1 @@
+--- !!str "Это уникодная строка"
diff --git a/_test/data/construct-str-utf8-py3.code b/_test/data/construct-str-utf8-py3.code
new file mode 100644
index 0000000..9f66032
--- /dev/null
+++ b/_test/data/construct-str-utf8-py3.code
@@ -0,0 +1 @@
+'\u042d\u0442\u043e \u0443\u043d\u0438\u043a\u043e\u0434\u043d\u0430\u044f \u0441\u0442\u0440\u043e\u043a\u0430'
diff --git a/_test/data/construct-str-utf8-py3.data b/_test/data/construct-str-utf8-py3.data
new file mode 100644
index 0000000..e355f18
--- /dev/null
+++ b/_test/data/construct-str-utf8-py3.data
@@ -0,0 +1 @@
+--- !!str "Это уникодная строка"
diff --git a/_test/data/construct-str.code b/_test/data/construct-str.code
new file mode 100644
index 0000000..8d57214
--- /dev/null
+++ b/_test/data/construct-str.code
@@ -0,0 +1 @@
+{ "string": "abcd" }
diff --git a/_test/data/construct-str.data b/_test/data/construct-str.data
new file mode 100644
index 0000000..606ac6b
--- /dev/null
+++ b/_test/data/construct-str.data
@@ -0,0 +1 @@
+string: abcd
diff --git a/_test/data/construct-timestamp.code b/_test/data/construct-timestamp.code
new file mode 100644
index 0000000..ffc3b2f
--- /dev/null
+++ b/_test/data/construct-timestamp.code
@@ -0,0 +1,7 @@
+{
+ "canonical": datetime.datetime(2001, 12, 15, 2, 59, 43, 100000),
+ "valid iso8601": datetime.datetime(2001, 12, 15, 2, 59, 43, 100000),
+ "space separated": datetime.datetime(2001, 12, 15, 2, 59, 43, 100000),
+ "no time zone (Z)": datetime.datetime(2001, 12, 15, 2, 59, 43, 100000),
+ "date (00:00:00Z)": datetime.date(2002, 12, 14),
+}
diff --git a/_test/data/construct-timestamp.data b/_test/data/construct-timestamp.data
new file mode 100644
index 0000000..c5f3840
--- /dev/null
+++ b/_test/data/construct-timestamp.data
@@ -0,0 +1,5 @@
+canonical: 2001-12-15T02:59:43.1Z
+valid iso8601: 2001-12-14t21:59:43.10-05:00
+space separated: 2001-12-14 21:59:43.10 -5
+no time zone (Z): 2001-12-15 2:59:43.10
+date (00:00:00Z): 2002-12-14
diff --git a/_test/data/construct-value.code b/_test/data/construct-value.code
new file mode 100644
index 0000000..f1f015e
--- /dev/null
+++ b/_test/data/construct-value.code
@@ -0,0 +1,9 @@
+[
+ { "link with": [ "library1.dll", "library2.dll" ] },
+ {
+ "link with": [
+ { "=": "library1.dll", "version": 1.2 },
+ { "=": "library2.dll", "version": 2.3 },
+ ],
+ },
+]
diff --git a/_test/data/construct-value.data b/_test/data/construct-value.data
new file mode 100644
index 0000000..3eb7919
--- /dev/null
+++ b/_test/data/construct-value.data
@@ -0,0 +1,10 @@
+--- # Old schema
+link with:
+ - library1.dll
+ - library2.dll
+--- # New schema
+link with:
+ - = : library1.dll
+ version: 1.2
+ - = : library2.dll
+ version: 2.3
diff --git a/_test/data/document-separator-in-quoted-scalar.loader-error b/_test/data/document-separator-in-quoted-scalar.loader-error
new file mode 100644
index 0000000..9eeb0d6
--- /dev/null
+++ b/_test/data/document-separator-in-quoted-scalar.loader-error
@@ -0,0 +1,11 @@
+---
+"this --- is correct"
+---
+"this
+...is also
+correct"
+---
+"a quoted scalar
+cannot contain
+---
+document separators"
diff --git a/_test/data/documents.events b/_test/data/documents.events
new file mode 100644
index 0000000..775a51a
--- /dev/null
+++ b/_test/data/documents.events
@@ -0,0 +1,11 @@
+- !StreamStart
+- !DocumentStart { explicit: false }
+- !Scalar { implicit: [true,false], value: 'data' }
+- !DocumentEnd
+- !DocumentStart
+- !Scalar { implicit: [true,false] }
+- !DocumentEnd
+- !DocumentStart { version: [1,1], tags: { '!': '!foo', '!yaml!': 'tag:yaml.org,2002:', '!ugly!': '!!!!!!!' } }
+- !Scalar { implicit: [true,false] }
+- !DocumentEnd
+- !StreamEnd
diff --git a/_test/data/duplicate-anchor-1.loader-warning b/_test/data/duplicate-anchor-1.loader-warning
new file mode 100644
index 0000000..906cf29
--- /dev/null
+++ b/_test/data/duplicate-anchor-1.loader-warning
@@ -0,0 +1,3 @@
+- &foo bar
+- &bar bar
+- &foo bar
diff --git a/_test/data/duplicate-anchor-2.loader-warning b/_test/data/duplicate-anchor-2.loader-warning
new file mode 100644
index 0000000..62b4389
--- /dev/null
+++ b/_test/data/duplicate-anchor-2.loader-warning
@@ -0,0 +1 @@
+&foo [1, 2, 3, &foo 4]
diff --git a/_test/data/duplicate-merge-key.former-loader-error.code b/_test/data/duplicate-merge-key.former-loader-error.code
new file mode 100644
index 0000000..6a757f3
--- /dev/null
+++ b/_test/data/duplicate-merge-key.former-loader-error.code
@@ -0,0 +1 @@
+{ 'x': 1, 'y': 2, 'foo': 'bar', 'z': 3, 't': 4 }
diff --git a/_test/data/duplicate-tag-directive.loader-error b/_test/data/duplicate-tag-directive.loader-error
new file mode 100644
index 0000000..50c81a0
--- /dev/null
+++ b/_test/data/duplicate-tag-directive.loader-error
@@ -0,0 +1,3 @@
+%TAG !foo! bar
+%TAG !foo! baz
+--- foo
diff --git a/_test/data/duplicate-yaml-directive.loader-error b/_test/data/duplicate-yaml-directive.loader-error
new file mode 100644
index 0000000..9b72390
--- /dev/null
+++ b/_test/data/duplicate-yaml-directive.loader-error
@@ -0,0 +1,3 @@
+%YAML 1.1
+%YAML 1.1
+--- foo
diff --git a/_test/data/emit-block-scalar-in-simple-key-context-bug.canonical b/_test/data/emit-block-scalar-in-simple-key-context-bug.canonical
new file mode 100644
index 0000000..473bed5
--- /dev/null
+++ b/_test/data/emit-block-scalar-in-simple-key-context-bug.canonical
@@ -0,0 +1,6 @@
+%YAML 1.1
+--- !!map
+{
+ ? !!str "foo"
+ : !!str "bar"
+}
diff --git a/_test/data/emit-block-scalar-in-simple-key-context-bug.data b/_test/data/emit-block-scalar-in-simple-key-context-bug.data
new file mode 100644
index 0000000..b6b42ba
--- /dev/null
+++ b/_test/data/emit-block-scalar-in-simple-key-context-bug.data
@@ -0,0 +1,4 @@
+? |-
+ foo
+: |-
+ bar
diff --git a/_test/data/emitting-unacceptable-unicode-character-bug-py2.code b/_test/data/emitting-unacceptable-unicode-character-bug-py2.code
new file mode 100644
index 0000000..4b92854
--- /dev/null
+++ b/_test/data/emitting-unacceptable-unicode-character-bug-py2.code
@@ -0,0 +1 @@
+u"\udd00"
diff --git a/_test/data/emitting-unacceptable-unicode-character-bug-py2.data b/_test/data/emitting-unacceptable-unicode-character-bug-py2.data
new file mode 100644
index 0000000..2a5df00
--- /dev/null
+++ b/_test/data/emitting-unacceptable-unicode-character-bug-py2.data
@@ -0,0 +1 @@
+"\udd00"
diff --git a/_test/data/emitting-unacceptable-unicode-character-bug-py2.skip-ext b/_test/data/emitting-unacceptable-unicode-character-bug-py2.skip-ext
new file mode 100644
index 0000000..e69de29
--- /dev/null
+++ b/_test/data/emitting-unacceptable-unicode-character-bug-py2.skip-ext
diff --git a/_test/data/emitting-unacceptable-unicode-character-bug-py3.code b/_test/data/emitting-unacceptable-unicode-character-bug-py3.code
new file mode 100644
index 0000000..2a5df00
--- /dev/null
+++ b/_test/data/emitting-unacceptable-unicode-character-bug-py3.code
@@ -0,0 +1 @@
+"\udd00"
diff --git a/_test/data/emitting-unacceptable-unicode-character-bug-py3.data b/_test/data/emitting-unacceptable-unicode-character-bug-py3.data
new file mode 100644
index 0000000..2a5df00
--- /dev/null
+++ b/_test/data/emitting-unacceptable-unicode-character-bug-py3.data
@@ -0,0 +1 @@
+"\udd00"
diff --git a/_test/data/emitting-unacceptable-unicode-character-bug-py3.skip-ext b/_test/data/emitting-unacceptable-unicode-character-bug-py3.skip-ext
new file mode 100644
index 0000000..e69de29
--- /dev/null
+++ b/_test/data/emitting-unacceptable-unicode-character-bug-py3.skip-ext
diff --git a/_test/data/empty-anchor.emitter-error b/_test/data/empty-anchor.emitter-error
new file mode 100644
index 0000000..ce663b6
--- /dev/null
+++ b/_test/data/empty-anchor.emitter-error
@@ -0,0 +1,5 @@
+- !StreamStart
+- !DocumentStart
+- !Scalar { anchor: '', value: 'foo' }
+- !DocumentEnd
+- !StreamEnd
diff --git a/_test/data/empty-document-bug.canonical b/_test/data/empty-document-bug.canonical
new file mode 100644
index 0000000..28a6cf1
--- /dev/null
+++ b/_test/data/empty-document-bug.canonical
@@ -0,0 +1 @@
+# This YAML stream contains no YAML documents.
diff --git a/_test/data/empty-document-bug.data b/_test/data/empty-document-bug.data
new file mode 100644
index 0000000..e69de29
--- /dev/null
+++ b/_test/data/empty-document-bug.data
diff --git a/_test/data/empty-document-bug.empty b/_test/data/empty-document-bug.empty
new file mode 100644
index 0000000..e69de29
--- /dev/null
+++ b/_test/data/empty-document-bug.empty
diff --git a/_test/data/empty-documents.single-loader-error b/_test/data/empty-documents.single-loader-error
new file mode 100644
index 0000000..f8dba8d
--- /dev/null
+++ b/_test/data/empty-documents.single-loader-error
@@ -0,0 +1,2 @@
+--- # first document
+--- # second document
diff --git a/_test/data/empty-python-module.loader-error b/_test/data/empty-python-module.loader-error
new file mode 100644
index 0000000..83d3232
--- /dev/null
+++ b/_test/data/empty-python-module.loader-error
@@ -0,0 +1 @@
+--- !!python:module:
diff --git a/_test/data/empty-python-name.loader-error b/_test/data/empty-python-name.loader-error
new file mode 100644
index 0000000..6162957
--- /dev/null
+++ b/_test/data/empty-python-name.loader-error
@@ -0,0 +1 @@
+--- !!python/name: empty
diff --git a/_test/data/empty-tag-handle.emitter-error b/_test/data/empty-tag-handle.emitter-error
new file mode 100644
index 0000000..235c899
--- /dev/null
+++ b/_test/data/empty-tag-handle.emitter-error
@@ -0,0 +1,5 @@
+- !StreamStart
+- !DocumentStart { tags: { '': 'bar' } }
+- !Scalar { value: 'foo' }
+- !DocumentEnd
+- !StreamEnd
diff --git a/_test/data/empty-tag-prefix.emitter-error b/_test/data/empty-tag-prefix.emitter-error
new file mode 100644
index 0000000..c6c0e95
--- /dev/null
+++ b/_test/data/empty-tag-prefix.emitter-error
@@ -0,0 +1,5 @@
+- !StreamStart
+- !DocumentStart { tags: { '!': '' } }
+- !Scalar { value: 'foo' }
+- !DocumentEnd
+- !StreamEnd
diff --git a/_test/data/empty-tag.emitter-error b/_test/data/empty-tag.emitter-error
new file mode 100644
index 0000000..b7ca593
--- /dev/null
+++ b/_test/data/empty-tag.emitter-error
@@ -0,0 +1,5 @@
+- !StreamStart
+- !DocumentStart
+- !Scalar { tag: '', value: 'key', implicit: [false,false] }
+- !DocumentEnd
+- !StreamEnd
diff --git a/_test/data/expected-document-end.emitter-error b/_test/data/expected-document-end.emitter-error
new file mode 100644
index 0000000..0cbab89
--- /dev/null
+++ b/_test/data/expected-document-end.emitter-error
@@ -0,0 +1,6 @@
+- !StreamStart
+- !DocumentStart
+- !Scalar { value: 'data 1' }
+- !Scalar { value: 'data 2' }
+- !DocumentEnd
+- !StreamEnd
diff --git a/_test/data/expected-document-start.emitter-error b/_test/data/expected-document-start.emitter-error
new file mode 100644
index 0000000..8ce575e
--- /dev/null
+++ b/_test/data/expected-document-start.emitter-error
@@ -0,0 +1,4 @@
+- !StreamStart
+- !MappingStart
+- !MappingEnd
+- !StreamEnd
diff --git a/_test/data/expected-mapping.loader-error b/_test/data/expected-mapping.loader-error
new file mode 100644
index 0000000..82aed98
--- /dev/null
+++ b/_test/data/expected-mapping.loader-error
@@ -0,0 +1 @@
+--- !!map [not, a, map]
diff --git a/_test/data/expected-node-1.emitter-error b/_test/data/expected-node-1.emitter-error
new file mode 100644
index 0000000..36ceca3
--- /dev/null
+++ b/_test/data/expected-node-1.emitter-error
@@ -0,0 +1,4 @@
+- !StreamStart
+- !DocumentStart
+- !DocumentEnd
+- !StreamEnd
diff --git a/_test/data/expected-node-2.emitter-error b/_test/data/expected-node-2.emitter-error
new file mode 100644
index 0000000..891ee37
--- /dev/null
+++ b/_test/data/expected-node-2.emitter-error
@@ -0,0 +1,7 @@
+- !StreamStart
+- !DocumentStart
+- !MappingStart
+- !Scalar { value: 'key' }
+- !MappingEnd
+- !DocumentEnd
+- !StreamEnd
diff --git a/_test/data/expected-nothing.emitter-error b/_test/data/expected-nothing.emitter-error
new file mode 100644
index 0000000..62c54d3
--- /dev/null
+++ b/_test/data/expected-nothing.emitter-error
@@ -0,0 +1,4 @@
+- !StreamStart
+- !StreamEnd
+- !StreamStart
+- !StreamEnd
diff --git a/_test/data/expected-scalar.loader-error b/_test/data/expected-scalar.loader-error
new file mode 100644
index 0000000..7b3171e
--- /dev/null
+++ b/_test/data/expected-scalar.loader-error
@@ -0,0 +1 @@
+--- !!str [not a scalar]
diff --git a/_test/data/expected-sequence.loader-error b/_test/data/expected-sequence.loader-error
new file mode 100644
index 0000000..08074ea
--- /dev/null
+++ b/_test/data/expected-sequence.loader-error
@@ -0,0 +1 @@
+--- !!seq {foo, bar, baz}
diff --git a/_test/data/expected-stream-start.emitter-error b/_test/data/expected-stream-start.emitter-error
new file mode 100644
index 0000000..480dc2e
--- /dev/null
+++ b/_test/data/expected-stream-start.emitter-error
@@ -0,0 +1,2 @@
+- !DocumentStart
+- !DocumentEnd
diff --git a/_test/data/explicit-document.single-loader-error b/_test/data/explicit-document.single-loader-error
new file mode 100644
index 0000000..46c6f8b
--- /dev/null
+++ b/_test/data/explicit-document.single-loader-error
@@ -0,0 +1,4 @@
+---
+foo: bar
+---
+foo: bar
diff --git a/_test/data/fetch-complex-value-bug.loader-error b/_test/data/fetch-complex-value-bug.loader-error
new file mode 100644
index 0000000..25fac24
--- /dev/null
+++ b/_test/data/fetch-complex-value-bug.loader-error
@@ -0,0 +1,2 @@
+? "foo"
+ : "bar"
diff --git a/_test/data/float-representer-2.3-bug.code b/_test/data/float-representer-2.3-bug.code
new file mode 100644
index 0000000..d8db834
--- /dev/null
+++ b/_test/data/float-representer-2.3-bug.code
@@ -0,0 +1,7 @@
+{
+# 0.0: 0,
+ 1.0: 1,
+ 1e300000: +10,
+ -1e300000: -10,
+ 1e300000/1e300000: 100,
+}
diff --git a/_test/data/float-representer-2.3-bug.data b/_test/data/float-representer-2.3-bug.data
new file mode 100644
index 0000000..efd1716
--- /dev/null
+++ b/_test/data/float-representer-2.3-bug.data
@@ -0,0 +1,5 @@
+#0.0: # hash(0) == hash(nan) and 0 == nan in Python 2.3
+1.0: 1
++.inf: 10
+-.inf: -10
+.nan: 100
diff --git a/_test/data/float.data b/_test/data/float.data
new file mode 100644
index 0000000..524d5db
--- /dev/null
+++ b/_test/data/float.data
@@ -0,0 +1,6 @@
+- 6.8523015e+5
+- 685.230_15e+03
+- 685_230.15
+- 190:20:30.15
+- -.inf
+- .NaN
diff --git a/_test/data/float.detect b/_test/data/float.detect
new file mode 100644
index 0000000..1e12343
--- /dev/null
+++ b/_test/data/float.detect
@@ -0,0 +1 @@
+tag:yaml.org,2002:float
diff --git a/_test/data/forbidden-entry.loader-error b/_test/data/forbidden-entry.loader-error
new file mode 100644
index 0000000..f2e3079
--- /dev/null
+++ b/_test/data/forbidden-entry.loader-error
@@ -0,0 +1,2 @@
+test: - foo
+ - bar
diff --git a/_test/data/forbidden-key.loader-error b/_test/data/forbidden-key.loader-error
new file mode 100644
index 0000000..da9b471
--- /dev/null
+++ b/_test/data/forbidden-key.loader-error
@@ -0,0 +1,2 @@
+test: ? foo
+ : bar
diff --git a/_test/data/forbidden-value.loader-error b/_test/data/forbidden-value.loader-error
new file mode 100644
index 0000000..efd7ce5
--- /dev/null
+++ b/_test/data/forbidden-value.loader-error
@@ -0,0 +1 @@
+test: key: value
diff --git a/_test/data/implicit-document.single-loader-error b/_test/data/implicit-document.single-loader-error
new file mode 100644
index 0000000..f8c9a5c
--- /dev/null
+++ b/_test/data/implicit-document.single-loader-error
@@ -0,0 +1,3 @@
+foo: bar
+---
+foo: bar
diff --git a/_test/data/int.data b/_test/data/int.data
new file mode 100644
index 0000000..f71d814
--- /dev/null
+++ b/_test/data/int.data
@@ -0,0 +1,7 @@
+- 685230
+- +685_230
+- 02472256
+- 0o2472256
+- 0x_0A_74_AE
+- 0b1010_0111_0100_1010_1110
+- 190:20:30
diff --git a/_test/data/int.detect b/_test/data/int.detect
new file mode 100644
index 0000000..575c9eb
--- /dev/null
+++ b/_test/data/int.detect
@@ -0,0 +1 @@
+tag:yaml.org,2002:int
diff --git a/_test/data/invalid-anchor-1.loader-error b/_test/data/invalid-anchor-1.loader-error
new file mode 100644
index 0000000..fcf7d0f
--- /dev/null
+++ b/_test/data/invalid-anchor-1.loader-error
@@ -0,0 +1 @@
+--- &? foo # we allow only ascii and numeric characters in anchor names.
diff --git a/_test/data/invalid-anchor-2.loader-error b/_test/data/invalid-anchor-2.loader-error
new file mode 100644
index 0000000..bfc4ff0
--- /dev/null
+++ b/_test/data/invalid-anchor-2.loader-error
@@ -0,0 +1,8 @@
+---
+- [
+ &correct foo,
+ *correct,
+ *correct] # still correct
+- *correct: still correct
+- &correct-or-not[foo, bar]
+
diff --git a/_test/data/invalid-anchor.emitter-error b/_test/data/invalid-anchor.emitter-error
new file mode 100644
index 0000000..3d2a814
--- /dev/null
+++ b/_test/data/invalid-anchor.emitter-error
@@ -0,0 +1,5 @@
+- !StreamStart
+- !DocumentStart
+- !Scalar { anchor: '5*5=25', value: 'foo' }
+- !DocumentEnd
+- !StreamEnd
diff --git a/_test/data/invalid-base64-data-2.loader-error b/_test/data/invalid-base64-data-2.loader-error
new file mode 100644
index 0000000..2553a4f
--- /dev/null
+++ b/_test/data/invalid-base64-data-2.loader-error
@@ -0,0 +1,2 @@
+--- !!binary
+ двоичные данные в base64
diff --git a/_test/data/invalid-base64-data.loader-error b/_test/data/invalid-base64-data.loader-error
new file mode 100644
index 0000000..798abba
--- /dev/null
+++ b/_test/data/invalid-base64-data.loader-error
@@ -0,0 +1,2 @@
+--- !!binary
+ binary data encoded in base64 should be here.
diff --git a/_test/data/invalid-block-scalar-indicator.loader-error b/_test/data/invalid-block-scalar-indicator.loader-error
new file mode 100644
index 0000000..16a6db1
--- /dev/null
+++ b/_test/data/invalid-block-scalar-indicator.loader-error
@@ -0,0 +1,2 @@
+--- > what is this? # a comment
+data
diff --git a/_test/data/invalid-character.loader-error b/_test/data/invalid-character.loader-error
new file mode 100644
index 0000000..03687b0
--- /dev/null
+++ b/_test/data/invalid-character.loader-error
Binary files differ
diff --git a/_test/data/invalid-character.stream-error b/_test/data/invalid-character.stream-error
new file mode 100644
index 0000000..171face
--- /dev/null
+++ b/_test/data/invalid-character.stream-error
Binary files differ
diff --git a/_test/data/invalid-directive-line.loader-error b/_test/data/invalid-directive-line.loader-error
new file mode 100644
index 0000000..0892eb6
--- /dev/null
+++ b/_test/data/invalid-directive-line.loader-error
@@ -0,0 +1,2 @@
+%YAML 1.1 ? # extra symbol
+---
diff --git a/_test/data/invalid-directive-name-1.loader-error b/_test/data/invalid-directive-name-1.loader-error
new file mode 100644
index 0000000..153fd88
--- /dev/null
+++ b/_test/data/invalid-directive-name-1.loader-error
@@ -0,0 +1,2 @@
+% # no name at all
+---
diff --git a/_test/data/invalid-directive-name-2.loader-error b/_test/data/invalid-directive-name-2.loader-error
new file mode 100644
index 0000000..3732a06
--- /dev/null
+++ b/_test/data/invalid-directive-name-2.loader-error
@@ -0,0 +1,2 @@
+%invalid-characters:in-directive name
+---
diff --git a/_test/data/invalid-escape-character.loader-error b/_test/data/invalid-escape-character.loader-error
new file mode 100644
index 0000000..a95ab76
--- /dev/null
+++ b/_test/data/invalid-escape-character.loader-error
@@ -0,0 +1 @@
+"some escape characters are \ncorrect, but this one \?\nis not\n"
diff --git a/_test/data/invalid-escape-numbers.loader-error b/_test/data/invalid-escape-numbers.loader-error
new file mode 100644
index 0000000..614ec9f
--- /dev/null
+++ b/_test/data/invalid-escape-numbers.loader-error
@@ -0,0 +1 @@
+"hm.... \u123?"
diff --git a/_test/data/invalid-indentation-indicator-1.loader-error b/_test/data/invalid-indentation-indicator-1.loader-error
new file mode 100644
index 0000000..a3cd12f
--- /dev/null
+++ b/_test/data/invalid-indentation-indicator-1.loader-error
@@ -0,0 +1,2 @@
+--- >0 # not valid
+data
diff --git a/_test/data/invalid-indentation-indicator-2.loader-error b/_test/data/invalid-indentation-indicator-2.loader-error
new file mode 100644
index 0000000..eefb6ec
--- /dev/null
+++ b/_test/data/invalid-indentation-indicator-2.loader-error
@@ -0,0 +1,2 @@
+--- >-0
+data
diff --git a/_test/data/invalid-item-without-trailing-break.loader-error b/_test/data/invalid-item-without-trailing-break.loader-error
new file mode 100644
index 0000000..fdcf6c6
--- /dev/null
+++ b/_test/data/invalid-item-without-trailing-break.loader-error
@@ -0,0 +1,2 @@
+-
+-0 \ No newline at end of file
diff --git a/_test/data/invalid-merge-1.loader-error b/_test/data/invalid-merge-1.loader-error
new file mode 100644
index 0000000..fc3c284
--- /dev/null
+++ b/_test/data/invalid-merge-1.loader-error
@@ -0,0 +1,2 @@
+foo: bar
+<<: baz
diff --git a/_test/data/invalid-merge-2.loader-error b/_test/data/invalid-merge-2.loader-error
new file mode 100644
index 0000000..8e88615
--- /dev/null
+++ b/_test/data/invalid-merge-2.loader-error
@@ -0,0 +1,2 @@
+foo: bar
+<<: [x: 1, y: 2, z, t: 4]
diff --git a/_test/data/invalid-omap-1.loader-error b/_test/data/invalid-omap-1.loader-error
new file mode 100644
index 0000000..2863392
--- /dev/null
+++ b/_test/data/invalid-omap-1.loader-error
@@ -0,0 +1,3 @@
+--- !!omap
+foo: bar
+baz: bat
diff --git a/_test/data/invalid-omap-2.loader-error b/_test/data/invalid-omap-2.loader-error
new file mode 100644
index 0000000..c377dfb
--- /dev/null
+++ b/_test/data/invalid-omap-2.loader-error
@@ -0,0 +1,3 @@
+--- !!omap
+- foo: bar
+- baz
diff --git a/_test/data/invalid-omap-3.loader-error b/_test/data/invalid-omap-3.loader-error
new file mode 100644
index 0000000..2a4f50d
--- /dev/null
+++ b/_test/data/invalid-omap-3.loader-error
@@ -0,0 +1,4 @@
+--- !!omap
+- foo: bar
+- baz: bar
+ bar: bar
diff --git a/_test/data/invalid-pairs-1.loader-error b/_test/data/invalid-pairs-1.loader-error
new file mode 100644
index 0000000..42d19ae
--- /dev/null
+++ b/_test/data/invalid-pairs-1.loader-error
@@ -0,0 +1,3 @@
+--- !!pairs
+foo: bar
+baz: bat
diff --git a/_test/data/invalid-pairs-2.loader-error b/_test/data/invalid-pairs-2.loader-error
new file mode 100644
index 0000000..31389ea
--- /dev/null
+++ b/_test/data/invalid-pairs-2.loader-error
@@ -0,0 +1,3 @@
+--- !!pairs
+- foo: bar
+- baz
diff --git a/_test/data/invalid-pairs-3.loader-error b/_test/data/invalid-pairs-3.loader-error
new file mode 100644
index 0000000..f8d7704
--- /dev/null
+++ b/_test/data/invalid-pairs-3.loader-error
@@ -0,0 +1,4 @@
+--- !!pairs
+- foo: bar
+- baz: bar
+ bar: bar
diff --git a/_test/data/invalid-python-bytes-2-py3.loader-error b/_test/data/invalid-python-bytes-2-py3.loader-error
new file mode 100644
index 0000000..f43af59
--- /dev/null
+++ b/_test/data/invalid-python-bytes-2-py3.loader-error
@@ -0,0 +1,2 @@
+--- !!python/bytes
+ двоичные данные в base64
diff --git a/_test/data/invalid-python-bytes-py3.loader-error b/_test/data/invalid-python-bytes-py3.loader-error
new file mode 100644
index 0000000..a19dfd0
--- /dev/null
+++ b/_test/data/invalid-python-bytes-py3.loader-error
@@ -0,0 +1,2 @@
+--- !!python/bytes
+ binary data encoded in base64 should be here.
diff --git a/_test/data/invalid-python-module-kind.loader-error b/_test/data/invalid-python-module-kind.loader-error
new file mode 100644
index 0000000..4f71cb5
--- /dev/null
+++ b/_test/data/invalid-python-module-kind.loader-error
@@ -0,0 +1 @@
+--- !!python/module:sys { must, be, scalar }
diff --git a/_test/data/invalid-python-module-value.loader-error b/_test/data/invalid-python-module-value.loader-error
new file mode 100644
index 0000000..f6797fc
--- /dev/null
+++ b/_test/data/invalid-python-module-value.loader-error
@@ -0,0 +1 @@
+--- !!python/module:sys "non-empty value"
diff --git a/_test/data/invalid-python-module.loader-error b/_test/data/invalid-python-module.loader-error
new file mode 100644
index 0000000..4e24072
--- /dev/null
+++ b/_test/data/invalid-python-module.loader-error
@@ -0,0 +1 @@
+--- !!python/module:no.such.module
diff --git a/_test/data/invalid-python-name-kind.loader-error b/_test/data/invalid-python-name-kind.loader-error
new file mode 100644
index 0000000..6ff8eb6
--- /dev/null
+++ b/_test/data/invalid-python-name-kind.loader-error
@@ -0,0 +1 @@
+--- !!python/name:sys.modules {}
diff --git a/_test/data/invalid-python-name-module-2.loader-error b/_test/data/invalid-python-name-module-2.loader-error
new file mode 100644
index 0000000..debc313
--- /dev/null
+++ b/_test/data/invalid-python-name-module-2.loader-error
@@ -0,0 +1 @@
+--- !!python/name:xml.parsers
diff --git a/_test/data/invalid-python-name-module.loader-error b/_test/data/invalid-python-name-module.loader-error
new file mode 100644
index 0000000..1966f6a
--- /dev/null
+++ b/_test/data/invalid-python-name-module.loader-error
@@ -0,0 +1 @@
+--- !!python/name:sys.modules.keys
diff --git a/_test/data/invalid-python-name-object.loader-error b/_test/data/invalid-python-name-object.loader-error
new file mode 100644
index 0000000..50f386f
--- /dev/null
+++ b/_test/data/invalid-python-name-object.loader-error
@@ -0,0 +1 @@
+--- !!python/name:os.path.rm_rf
diff --git a/_test/data/invalid-python-name-value.loader-error b/_test/data/invalid-python-name-value.loader-error
new file mode 100644
index 0000000..7be1401
--- /dev/null
+++ b/_test/data/invalid-python-name-value.loader-error
@@ -0,0 +1 @@
+--- !!python/name:sys.modules 5
diff --git a/_test/data/invalid-simple-key.loader-error b/_test/data/invalid-simple-key.loader-error
new file mode 100644
index 0000000..a58deec
--- /dev/null
+++ b/_test/data/invalid-simple-key.loader-error
@@ -0,0 +1,3 @@
+key: value
+invalid simple key
+next key: next value
diff --git a/_test/data/invalid-single-quote-bug.code b/_test/data/invalid-single-quote-bug.code
new file mode 100644
index 0000000..5558945
--- /dev/null
+++ b/_test/data/invalid-single-quote-bug.code
@@ -0,0 +1 @@
+["foo 'bar'", "foo\n'bar'"]
diff --git a/_test/data/invalid-single-quote-bug.data b/_test/data/invalid-single-quote-bug.data
new file mode 100644
index 0000000..76ef7ae
--- /dev/null
+++ b/_test/data/invalid-single-quote-bug.data
@@ -0,0 +1,2 @@
+- "foo 'bar'"
+- "foo\n'bar'"
diff --git a/_test/data/invalid-starting-character.loader-error b/_test/data/invalid-starting-character.loader-error
new file mode 100644
index 0000000..bb81c60
--- /dev/null
+++ b/_test/data/invalid-starting-character.loader-error
@@ -0,0 +1 @@
+@@@@@@@@@@@@@@@@@@@
diff --git a/_test/data/invalid-tag-1.loader-error b/_test/data/invalid-tag-1.loader-error
new file mode 100644
index 0000000..a68cd38
--- /dev/null
+++ b/_test/data/invalid-tag-1.loader-error
@@ -0,0 +1 @@
+- !<foo#bar> baz
diff --git a/_test/data/invalid-tag-2.loader-error b/_test/data/invalid-tag-2.loader-error
new file mode 100644
index 0000000..3a36700
--- /dev/null
+++ b/_test/data/invalid-tag-2.loader-error
@@ -0,0 +1 @@
+- !prefix!foo#bar baz
diff --git a/_test/data/invalid-tag-directive-handle.loader-error b/_test/data/invalid-tag-directive-handle.loader-error
new file mode 100644
index 0000000..42b5d7e
--- /dev/null
+++ b/_test/data/invalid-tag-directive-handle.loader-error
@@ -0,0 +1,2 @@
+%TAG !!! !!!
+---
diff --git a/_test/data/invalid-tag-directive-prefix.loader-error b/_test/data/invalid-tag-directive-prefix.loader-error
new file mode 100644
index 0000000..0cb482c
--- /dev/null
+++ b/_test/data/invalid-tag-directive-prefix.loader-error
@@ -0,0 +1,2 @@
+%TAG ! tag:zz.com/foo#bar # '#' is not allowed in URLs
+---
diff --git a/_test/data/invalid-tag-handle-1.emitter-error b/_test/data/invalid-tag-handle-1.emitter-error
new file mode 100644
index 0000000..d5df9a2
--- /dev/null
+++ b/_test/data/invalid-tag-handle-1.emitter-error
@@ -0,0 +1,5 @@
+- !StreamStart
+- !DocumentStart { tags: { '!foo': 'bar' } }
+- !Scalar { value: 'foo' }
+- !DocumentEnd
+- !StreamEnd
diff --git a/_test/data/invalid-tag-handle-1.loader-error b/_test/data/invalid-tag-handle-1.loader-error
new file mode 100644
index 0000000..ef0d143
--- /dev/null
+++ b/_test/data/invalid-tag-handle-1.loader-error
@@ -0,0 +1,2 @@
+%TAG foo bar
+---
diff --git a/_test/data/invalid-tag-handle-2.emitter-error b/_test/data/invalid-tag-handle-2.emitter-error
new file mode 100644
index 0000000..d1831d5
--- /dev/null
+++ b/_test/data/invalid-tag-handle-2.emitter-error
@@ -0,0 +1,5 @@
+- !StreamStart
+- !DocumentStart { tags: { '!!!': 'bar' } }
+- !Scalar { value: 'foo' }
+- !DocumentEnd
+- !StreamEnd
diff --git a/_test/data/invalid-tag-handle-2.loader-error b/_test/data/invalid-tag-handle-2.loader-error
new file mode 100644
index 0000000..06c7f0e
--- /dev/null
+++ b/_test/data/invalid-tag-handle-2.loader-error
@@ -0,0 +1,2 @@
+%TAG !foo bar
+---
diff --git a/_test/data/invalid-uri-escapes-1.loader-error b/_test/data/invalid-uri-escapes-1.loader-error
new file mode 100644
index 0000000..a6ecb36
--- /dev/null
+++ b/_test/data/invalid-uri-escapes-1.loader-error
@@ -0,0 +1 @@
+--- !<tag:%x?y> foo
diff --git a/_test/data/invalid-uri-escapes-2.loader-error b/_test/data/invalid-uri-escapes-2.loader-error
new file mode 100644
index 0000000..b89e8f6
--- /dev/null
+++ b/_test/data/invalid-uri-escapes-2.loader-error
@@ -0,0 +1 @@
+--- !<%FF> foo
diff --git a/_test/data/invalid-uri-escapes-3.loader-error b/_test/data/invalid-uri-escapes-3.loader-error
new file mode 100644
index 0000000..f2e4cb8
--- /dev/null
+++ b/_test/data/invalid-uri-escapes-3.loader-error
@@ -0,0 +1 @@
+--- !<foo%d0%af%d0%af%d0bar> baz
diff --git a/_test/data/invalid-uri.loader-error b/_test/data/invalid-uri.loader-error
new file mode 100644
index 0000000..06307e0
--- /dev/null
+++ b/_test/data/invalid-uri.loader-error
@@ -0,0 +1 @@
+--- !foo! bar
diff --git a/_test/data/invalid-utf8-byte.loader-error b/_test/data/invalid-utf8-byte.loader-error
new file mode 100644
index 0000000..0a58c70
--- /dev/null
+++ b/_test/data/invalid-utf8-byte.loader-error
@@ -0,0 +1,66 @@
+###############################################################
+###############################################################
+###############################################################
+###############################################################
+###############################################################
+###############################################################
+###############################################################
+###############################################################
+###############################################################
+###############################################################
+###############################################################
+###############################################################
+###############################################################
+###############################################################
+###############################################################
+###############################################################
+###############################################################
+###############################################################
+###############################################################
+###############################################################
+###############################################################
+###############################################################
+###############################################################
+###############################################################
+###############################################################
+###############################################################
+###############################################################
+###############################################################
+###############################################################
+###############################################################
+###############################################################
+###############################################################
+###############################################################
+###############################################################
+###############################################################
+###############################################################
+###############################################################
+###############################################################
+###############################################################
+###############################################################
+###############################################################
+###############################################################
+###############################################################
+###############################################################
+###############################################################
+###############################################################
+###############################################################
+###############################################################
+###############################################################
+###############################################################
+###############################################################
+###############################################################
+###############################################################
+###############################################################
+###############################################################
+###############################################################
+###############################################################
+###############################################################
+###############################################################
+###############################################################
+###############################################################
+###############################################################
+###############################################################
+###############################################################
+Invalid byte ('\xFF'): <--
+###############################################################
diff --git a/_test/data/invalid-utf8-byte.stream-error b/_test/data/invalid-utf8-byte.stream-error
new file mode 100644
index 0000000..0a58c70
--- /dev/null
+++ b/_test/data/invalid-utf8-byte.stream-error
@@ -0,0 +1,66 @@
+###############################################################
+###############################################################
+###############################################################
+###############################################################
+###############################################################
+###############################################################
+###############################################################
+###############################################################
+###############################################################
+###############################################################
+###############################################################
+###############################################################
+###############################################################
+###############################################################
+###############################################################
+###############################################################
+###############################################################
+###############################################################
+###############################################################
+###############################################################
+###############################################################
+###############################################################
+###############################################################
+###############################################################
+###############################################################
+###############################################################
+###############################################################
+###############################################################
+###############################################################
+###############################################################
+###############################################################
+###############################################################
+###############################################################
+###############################################################
+###############################################################
+###############################################################
+###############################################################
+###############################################################
+###############################################################
+###############################################################
+###############################################################
+###############################################################
+###############################################################
+###############################################################
+###############################################################
+###############################################################
+###############################################################
+###############################################################
+###############################################################
+###############################################################
+###############################################################
+###############################################################
+###############################################################
+###############################################################
+###############################################################
+###############################################################
+###############################################################
+###############################################################
+###############################################################
+###############################################################
+###############################################################
+###############################################################
+###############################################################
+###############################################################
+Invalid byte ('\xFF'): <--
+###############################################################
diff --git a/_test/data/invalid-yaml-directive-version-1.loader-error b/_test/data/invalid-yaml-directive-version-1.loader-error
new file mode 100644
index 0000000..e9b4e3a
--- /dev/null
+++ b/_test/data/invalid-yaml-directive-version-1.loader-error
@@ -0,0 +1,3 @@
+# No version at all.
+%YAML
+---
diff --git a/_test/data/invalid-yaml-directive-version-2.loader-error b/_test/data/invalid-yaml-directive-version-2.loader-error
new file mode 100644
index 0000000..6aa7740
--- /dev/null
+++ b/_test/data/invalid-yaml-directive-version-2.loader-error
@@ -0,0 +1,2 @@
+%YAML 1e-5
+---
diff --git a/_test/data/invalid-yaml-directive-version-3.loader-error b/_test/data/invalid-yaml-directive-version-3.loader-error
new file mode 100644
index 0000000..345e784
--- /dev/null
+++ b/_test/data/invalid-yaml-directive-version-3.loader-error
@@ -0,0 +1,2 @@
+%YAML 1.
+---
diff --git a/_test/data/invalid-yaml-directive-version-4.loader-error b/_test/data/invalid-yaml-directive-version-4.loader-error
new file mode 100644
index 0000000..b35ca82
--- /dev/null
+++ b/_test/data/invalid-yaml-directive-version-4.loader-error
@@ -0,0 +1,2 @@
+%YAML 1.132.435
+---
diff --git a/_test/data/invalid-yaml-directive-version-5.loader-error b/_test/data/invalid-yaml-directive-version-5.loader-error
new file mode 100644
index 0000000..7c2b49f
--- /dev/null
+++ b/_test/data/invalid-yaml-directive-version-5.loader-error
@@ -0,0 +1,2 @@
+%YAML A.0
+---
diff --git a/_test/data/invalid-yaml-directive-version-6.loader-error b/_test/data/invalid-yaml-directive-version-6.loader-error
new file mode 100644
index 0000000..bae714f
--- /dev/null
+++ b/_test/data/invalid-yaml-directive-version-6.loader-error
@@ -0,0 +1,2 @@
+%YAML 123.C
+---
diff --git a/_test/data/invalid-yaml-version.loader-error b/_test/data/invalid-yaml-version.loader-error
new file mode 100644
index 0000000..dd01948
--- /dev/null
+++ b/_test/data/invalid-yaml-version.loader-error
@@ -0,0 +1,2 @@
+%YAML 2.0
+--- foo
diff --git a/_test/data/latin.unicode b/_test/data/latin.unicode
new file mode 100644
index 0000000..4fb799c
--- /dev/null
+++ b/_test/data/latin.unicode
@@ -0,0 +1,384 @@
+ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyzªµºÀÁÂÃÄÅÆÇÈÉÊ
+ËÌÍÎÏÐÑÒÓÔÕÖØÙÚÛÜÝÞßàáâãäåæçèéêëìíîïðñòóôõöøùúûüýþÿĀāĂ㥹ĆćĈĉĊċČčĎ
+ďĐđĒēĔĕĖėĘęĚěĜĝĞğĠġĢģĤĥĦħĨĩĪīĬĭĮįİıIJijĴĵĶķĸĹĺĻļĽľĿŀŁłŃńŅņŇňʼnŊŋŌōŎŏŐ
+őŒœŔŕŖŗŘřŚśŜŝŞşŠšŢţŤťŦŧŨũŪūŬŭŮůŰűŲųŴŵŶŷŸŹźŻżŽžſƀƁƂƃƄƅƆƇƈƉƊƋƌƍƎƏƐƑƒ
+ƓƔƕƖƗƘƙƚƛƜƝƞƟƠơƢƣƤƥƦƧƨƩƪƫƬƭƮƯưƱƲƳƴƵƶƷƸƹƺƼƽƾƿDŽdžLJljNJnjǍǎǏǐǑǒǓǔǕǖǗǘǙǚǛǜ
+ǝǞǟǠǡǢǣǤǥǦǧǨǩǪǫǬǭǮǯǰDZdzǴǵǶǷǸǹǺǻǼǽǾǿȀȁȂȃȄȅȆȇȈȉȊȋȌȍȎȏȐȑȒȓȔȕȖȗȘșȚțȜȝȞȟ
+ȠȡȢȣȤȥȦȧȨȩȪȫȬȭȮȯȰȱȲȳȴȵȶȷȸȹȺȻȼȽȾȿɀɁɐɑɒɓɔɕɖɗɘəɚɛɜɝɞɟɠɡɢɣɤɥɦɧɨɩɪɫɬɭɮɯ
+ɰɱɲɳɴɵɶɷɸɹɺɻɼɽɾɿʀʁʂʃʄʅʆʇʈʉʊʋʌʍʎʏʐʑʒʓʔʕʖʗʘʙʚʛʜʝʞʟʠʡʢʣʤʥʦʧʨʩʪʫʬʭʮʯΆΈ
+ΉΊΌΎΏΐΑΒΓΔΕΖΗΘΙΚΛΜΝΞΟΠΡΣΤΥΦΧΨΩΪΫάέήίΰαβγδεζηθικλμνξοπρςστυφχψωϊϋόύ
+ώϐϑϒϓϔϕϖϗϘϙϚϛϜϝϞϟϠϡϢϣϤϥϦϧϨϩϪϫϬϭϮϯϰϱϲϳϴϵϷϸϹϺϻϼϽϾϿЀЁЂЃЄЅІЇЈЉЊЋЌЍЎЏАБ
+ВГДЕЖЗИЙКЛМНОПРСТУФХЦЧШЩЪЫЬЭЮЯабвгдежзийклмнопрстуфхцчшщъыьэюяѐёђѓ
+єѕіїјљњћќѝўџѠѡѢѣѤѥѦѧѨѩѪѫѬѭѮѯѰѱѲѳѴѵѶѷѸѹѺѻѼѽѾѿҀҁҊҋҌҍҎҏҐґҒғҔҕҖҗҘҙҚқҜҝ
+ҞҟҠҡҢңҤҥҦҧҨҩҪҫҬҭҮүҰұҲҳҴҵҶҷҸҹҺһҼҽҾҿӀӁӂӃӄӅӆӇӈӉӊӋӌӍӎӐӑӒӓӔӕӖӗӘәӚӛӜӝӞӟӠ
+ӡӢӣӤӥӦӧӨөӪӫӬӭӮӯӰӱӲӳӴӵӶӷӸӹԀԁԂԃԄԅԆԇԈԉԊԋԌԍԎԏԱԲԳԴԵԶԷԸԹԺԻԼԽԾԿՀՁՂՃՄՅՆՇՈՉ
+ՊՋՌՍՎՏՐՑՒՓՔՕՖաբգդեզէըթժիլխծկհձղճմյնշոչպջռսվտրցւփքօֆևႠႡႢႣႤႥႦႧႨႩႪႫႬႭ
+ႮႯႰႱႲႳႴႵႶႷႸႹႺႻႼႽႾႿჀჁჂჃჄჅᴀᴁᴂᴃᴄᴅᴆᴇᴈᴉᴊᴋᴌᴍᴎᴏᴐᴑᴒᴓᴔᴕᴖᴗᴘᴙᴚᴛᴜᴝᴞᴟᴠᴡᴢᴣᴤᴥᴦᴧᴨᴩ
+ᴪᴫᵢᵣᵤᵥᵦᵧᵨᵩᵪᵫᵬᵭᵮᵯᵰᵱᵲᵳᵴᵵᵶᵷᵹᵺᵻᵼᵽᵾᵿᶀᶁᶂᶃᶄᶅᶆᶇᶈᶉᶊᶋᶌᶍᶎᶏᶐᶑᶒᶓᶔᶕᶖᶗᶘᶙᶚḀḁḂḃḄḅḆḇ
+ḈḉḊḋḌḍḎḏḐḑḒḓḔḕḖḗḘḙḚḛḜḝḞḟḠḡḢḣḤḥḦḧḨḩḪḫḬḭḮḯḰḱḲḳḴḵḶḷḸḹḺḻḼḽḾḿṀṁṂṃṄṅṆṇṈṉ
+ṊṋṌṍṎṏṐṑṒṓṔṕṖṗṘṙṚṛṜṝṞṟṠṡṢṣṤṥṦṧṨṩṪṫṬṭṮṯṰṱṲṳṴṵṶṷṸṹṺṻṼṽṾṿẀẁẂẃẄẅẆẇẈẉẊẋ
+ẌẍẎẏẐẑẒẓẔẕẖẗẘẙẚẛẠạẢảẤấẦầẨẩẪẫẬậẮắẰằẲẳẴẵẶặẸẹẺẻẼẽẾếỀềỂểỄễỆệỈỉỊịỌọỎỏỐố
+ỒồỔổỖỗỘộỚớỜờỞởỠỡỢợỤụỦủỨứỪừỬửỮữỰựỲỳỴỵỶỷỸỹἀἁἂἃἄἅἆἇἈἉἊἋἌἍἎἏἐἑἒἓἔἕἘἙἚἛ
+ἜἝἠἡἢἣἤἥἦἧἨἩἪἫἬἭἮἯἰἱἲἳἴἵἶἷἸἹἺἻἼἽἾἿὀὁὂὃὄὅὈὉὊὋὌὍὐὑὒὓὔὕὖὗὙὛὝὟὠὡὢὣὤὥὦὧ
+ὨὩὪὫὬὭὮὯὰάὲέὴήὶίὸόὺύὼώᾀᾁᾂᾃᾄᾅᾆᾇᾐᾑᾒᾓᾔᾕᾖᾗᾠᾡᾢᾣᾤᾥᾦᾧᾰᾱᾲᾳᾴᾶᾷᾸᾹᾺΆιῂῃῄῆῇῈΈῊ
+ΉῐῑῒΐῖῗῘῙῚΊῠῡῢΰῤῥῦῧῨῩῪΎῬῲῳῴῶῷῸΌῺΏⁱⁿℂℇℊℋℌℍℎℏℐℑℒℓℕℙℚℛℜℝℤΩℨKÅℬℭℯℰℱℳℴℹ
+ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyzªµºÀÁÂÃÄÅÆÇÈÉÊ
+ËÌÍÎÏÐÑÒÓÔÕÖØÙÚÛÜÝÞßàáâãäåæçèéêëìíîïðñòóôõöøùúûüýþÿĀāĂ㥹ĆćĈĉĊċČčĎ
+ďĐđĒēĔĕĖėĘęĚěĜĝĞğĠġĢģĤĥĦħĨĩĪīĬĭĮįİıIJijĴĵĶķĸĹĺĻļĽľĿŀŁłŃńŅņŇňʼnŊŋŌōŎŏŐ
+őŒœŔŕŖŗŘřŚśŜŝŞşŠšŢţŤťŦŧŨũŪūŬŭŮůŰűŲųŴŵŶŷŸŹźŻżŽžſƀƁƂƃƄƅƆƇƈƉƊƋƌƍƎƏƐƑƒ
+ƓƔƕƖƗƘƙƚƛƜƝƞƟƠơƢƣƤƥƦƧƨƩƪƫƬƭƮƯưƱƲƳƴƵƶƷƸƹƺƼƽƾƿDŽdžLJljNJnjǍǎǏǐǑǒǓǔǕǖǗǘǙǚǛǜ
+ǝǞǟǠǡǢǣǤǥǦǧǨǩǪǫǬǭǮǯǰDZdzǴǵǶǷǸǹǺǻǼǽǾǿȀȁȂȃȄȅȆȇȈȉȊȋȌȍȎȏȐȑȒȓȔȕȖȗȘșȚțȜȝȞȟ
+ȠȡȢȣȤȥȦȧȨȩȪȫȬȭȮȯȰȱȲȳȴȵȶȷȸȹȺȻȼȽȾȿɀɁɐɑɒɓɔɕɖɗɘəɚɛɜɝɞɟɠɡɢɣɤɥɦɧɨɩɪɫɬɭɮɯ
+ɰɱɲɳɴɵɶɷɸɹɺɻɼɽɾɿʀʁʂʃʄʅʆʇʈʉʊʋʌʍʎʏʐʑʒʓʔʕʖʗʘʙʚʛʜʝʞʟʠʡʢʣʤʥʦʧʨʩʪʫʬʭʮʯΆΈ
+ΉΊΌΎΏΐΑΒΓΔΕΖΗΘΙΚΛΜΝΞΟΠΡΣΤΥΦΧΨΩΪΫάέήίΰαβγδεζηθικλμνξοπρςστυφχψωϊϋόύ
+ώϐϑϒϓϔϕϖϗϘϙϚϛϜϝϞϟϠϡϢϣϤϥϦϧϨϩϪϫϬϭϮϯϰϱϲϳϴϵϷϸϹϺϻϼϽϾϿЀЁЂЃЄЅІЇЈЉЊЋЌЍЎЏАБ
+ВГДЕЖЗИЙКЛМНОПРСТУФХЦЧШЩЪЫЬЭЮЯабвгдежзийклмнопрстуфхцчшщъыьэюяѐёђѓ
+єѕіїјљњћќѝўџѠѡѢѣѤѥѦѧѨѩѪѫѬѭѮѯѰѱѲѳѴѵѶѷѸѹѺѻѼѽѾѿҀҁҊҋҌҍҎҏҐґҒғҔҕҖҗҘҙҚқҜҝ
+ҞҟҠҡҢңҤҥҦҧҨҩҪҫҬҭҮүҰұҲҳҴҵҶҷҸҹҺһҼҽҾҿӀӁӂӃӄӅӆӇӈӉӊӋӌӍӎӐӑӒӓӔӕӖӗӘәӚӛӜӝӞӟӠ
+ӡӢӣӤӥӦӧӨөӪӫӬӭӮӯӰӱӲӳӴӵӶӷӸӹԀԁԂԃԄԅԆԇԈԉԊԋԌԍԎԏԱԲԳԴԵԶԷԸԹԺԻԼԽԾԿՀՁՂՃՄՅՆՇՈՉ
+ՊՋՌՍՎՏՐՑՒՓՔՕՖաբգդեզէըթժիլխծկհձղճմյնշոչպջռսվտրցւփքօֆևႠႡႢႣႤႥႦႧႨႩႪႫႬႭ
+ႮႯႰႱႲႳႴႵႶႷႸႹႺႻႼႽႾႿჀჁჂჃჄჅᴀᴁᴂᴃᴄᴅᴆᴇᴈᴉᴊᴋᴌᴍᴎᴏᴐᴑᴒᴓᴔᴕᴖᴗᴘᴙᴚᴛᴜᴝᴞᴟᴠᴡᴢᴣᴤᴥᴦᴧᴨᴩ
+ᴪᴫᵢᵣᵤᵥᵦᵧᵨᵩᵪᵫᵬᵭᵮᵯᵰᵱᵲᵳᵴᵵᵶᵷᵹᵺᵻᵼᵽᵾᵿᶀᶁᶂᶃᶄᶅᶆᶇᶈᶉᶊᶋᶌᶍᶎᶏᶐᶑᶒᶓᶔᶕᶖᶗᶘᶙᶚḀḁḂḃḄḅḆḇ
+ḈḉḊḋḌḍḎḏḐḑḒḓḔḕḖḗḘḙḚḛḜḝḞḟḠḡḢḣḤḥḦḧḨḩḪḫḬḭḮḯḰḱḲḳḴḵḶḷḸḹḺḻḼḽḾḿṀṁṂṃṄṅṆṇṈṉ
+ṊṋṌṍṎṏṐṑṒṓṔṕṖṗṘṙṚṛṜṝṞṟṠṡṢṣṤṥṦṧṨṩṪṫṬṭṮṯṰṱṲṳṴṵṶṷṸṹṺṻṼṽṾṿẀẁẂẃẄẅẆẇẈẉẊẋ
+ẌẍẎẏẐẑẒẓẔẕẖẗẘẙẚẛẠạẢảẤấẦầẨẩẪẫẬậẮắẰằẲẳẴẵẶặẸẹẺẻẼẽẾếỀềỂểỄễỆệỈỉỊịỌọỎỏỐố
+ỒồỔổỖỗỘộỚớỜờỞởỠỡỢợỤụỦủỨứỪừỬửỮữỰựỲỳỴỵỶỷỸỹἀἁἂἃἄἅἆἇἈἉἊἋἌἍἎἏἐἑἒἓἔἕἘἙἚἛ
+ἜἝἠἡἢἣἤἥἦἧἨἩἪἫἬἭἮἯἰἱἲἳἴἵἶἷἸἹἺἻἼἽἾἿὀὁὂὃὄὅὈὉὊὋὌὍὐὑὒὓὔὕὖὗὙὛὝὟὠὡὢὣὤὥὦὧ
+ὨὩὪὫὬὭὮὯὰάὲέὴήὶίὸόὺύὼώᾀᾁᾂᾃᾄᾅᾆᾇᾐᾑᾒᾓᾔᾕᾖᾗᾠᾡᾢᾣᾤᾥᾦᾧᾰᾱᾲᾳᾴᾶᾷᾸᾹᾺΆιῂῃῄῆῇῈΈῊ
+ΉῐῑῒΐῖῗῘῙῚΊῠῡῢΰῤῥῦῧῨῩῪΎῬῲῳῴῶῷῸΌῺΏⁱⁿℂℇℊℋℌℍℎℏℐℑℒℓℕℙℚℛℜℝℤΩℨKÅℬℭℯℰℱℳℴℹ
+ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyzªµºÀÁÂÃÄÅÆÇÈÉÊ
+ËÌÍÎÏÐÑÒÓÔÕÖØÙÚÛÜÝÞßàáâãäåæçèéêëìíîïðñòóôõöøùúûüýþÿĀāĂ㥹ĆćĈĉĊċČčĎ
+ďĐđĒēĔĕĖėĘęĚěĜĝĞğĠġĢģĤĥĦħĨĩĪīĬĭĮįİıIJijĴĵĶķĸĹĺĻļĽľĿŀŁłŃńŅņŇňʼnŊŋŌōŎŏŐ
+őŒœŔŕŖŗŘřŚśŜŝŞşŠšŢţŤťŦŧŨũŪūŬŭŮůŰűŲųŴŵŶŷŸŹźŻżŽžſƀƁƂƃƄƅƆƇƈƉƊƋƌƍƎƏƐƑƒ
+ƓƔƕƖƗƘƙƚƛƜƝƞƟƠơƢƣƤƥƦƧƨƩƪƫƬƭƮƯưƱƲƳƴƵƶƷƸƹƺƼƽƾƿDŽdžLJljNJnjǍǎǏǐǑǒǓǔǕǖǗǘǙǚǛǜ
+ǝǞǟǠǡǢǣǤǥǦǧǨǩǪǫǬǭǮǯǰDZdzǴǵǶǷǸǹǺǻǼǽǾǿȀȁȂȃȄȅȆȇȈȉȊȋȌȍȎȏȐȑȒȓȔȕȖȗȘșȚțȜȝȞȟ
+ȠȡȢȣȤȥȦȧȨȩȪȫȬȭȮȯȰȱȲȳȴȵȶȷȸȹȺȻȼȽȾȿɀɁɐɑɒɓɔɕɖɗɘəɚɛɜɝɞɟɠɡɢɣɤɥɦɧɨɩɪɫɬɭɮɯ
+ɰɱɲɳɴɵɶɷɸɹɺɻɼɽɾɿʀʁʂʃʄʅʆʇʈʉʊʋʌʍʎʏʐʑʒʓʔʕʖʗʘʙʚʛʜʝʞʟʠʡʢʣʤʥʦʧʨʩʪʫʬʭʮʯΆΈ
+ΉΊΌΎΏΐΑΒΓΔΕΖΗΘΙΚΛΜΝΞΟΠΡΣΤΥΦΧΨΩΪΫάέήίΰαβγδεζηθικλμνξοπρςστυφχψωϊϋόύ
+ώϐϑϒϓϔϕϖϗϘϙϚϛϜϝϞϟϠϡϢϣϤϥϦϧϨϩϪϫϬϭϮϯϰϱϲϳϴϵϷϸϹϺϻϼϽϾϿЀЁЂЃЄЅІЇЈЉЊЋЌЍЎЏАБ
+ВГДЕЖЗИЙКЛМНОПРСТУФХЦЧШЩЪЫЬЭЮЯабвгдежзийклмнопрстуфхцчшщъыьэюяѐёђѓ
+єѕіїјљњћќѝўџѠѡѢѣѤѥѦѧѨѩѪѫѬѭѮѯѰѱѲѳѴѵѶѷѸѹѺѻѼѽѾѿҀҁҊҋҌҍҎҏҐґҒғҔҕҖҗҘҙҚқҜҝ
+ҞҟҠҡҢңҤҥҦҧҨҩҪҫҬҭҮүҰұҲҳҴҵҶҷҸҹҺһҼҽҾҿӀӁӂӃӄӅӆӇӈӉӊӋӌӍӎӐӑӒӓӔӕӖӗӘәӚӛӜӝӞӟӠ
+ӡӢӣӤӥӦӧӨөӪӫӬӭӮӯӰӱӲӳӴӵӶӷӸӹԀԁԂԃԄԅԆԇԈԉԊԋԌԍԎԏԱԲԳԴԵԶԷԸԹԺԻԼԽԾԿՀՁՂՃՄՅՆՇՈՉ
+ՊՋՌՍՎՏՐՑՒՓՔՕՖաբգդեզէըթժիլխծկհձղճմյնշոչպջռսվտրցւփքօֆևႠႡႢႣႤႥႦႧႨႩႪႫႬႭ
+ႮႯႰႱႲႳႴႵႶႷႸႹႺႻႼႽႾႿჀჁჂჃჄჅᴀᴁᴂᴃᴄᴅᴆᴇᴈᴉᴊᴋᴌᴍᴎᴏᴐᴑᴒᴓᴔᴕᴖᴗᴘᴙᴚᴛᴜᴝᴞᴟᴠᴡᴢᴣᴤᴥᴦᴧᴨᴩ
+ᴪᴫᵢᵣᵤᵥᵦᵧᵨᵩᵪᵫᵬᵭᵮᵯᵰᵱᵲᵳᵴᵵᵶᵷᵹᵺᵻᵼᵽᵾᵿᶀᶁᶂᶃᶄᶅᶆᶇᶈᶉᶊᶋᶌᶍᶎᶏᶐᶑᶒᶓᶔᶕᶖᶗᶘᶙᶚḀḁḂḃḄḅḆḇ
+ḈḉḊḋḌḍḎḏḐḑḒḓḔḕḖḗḘḙḚḛḜḝḞḟḠḡḢḣḤḥḦḧḨḩḪḫḬḭḮḯḰḱḲḳḴḵḶḷḸḹḺḻḼḽḾḿṀṁṂṃṄṅṆṇṈṉ
+ṊṋṌṍṎṏṐṑṒṓṔṕṖṗṘṙṚṛṜṝṞṟṠṡṢṣṤṥṦṧṨṩṪṫṬṭṮṯṰṱṲṳṴṵṶṷṸṹṺṻṼṽṾṿẀẁẂẃẄẅẆẇẈẉẊẋ
+ẌẍẎẏẐẑẒẓẔẕẖẗẘẙẚẛẠạẢảẤấẦầẨẩẪẫẬậẮắẰằẲẳẴẵẶặẸẹẺẻẼẽẾếỀềỂểỄễỆệỈỉỊịỌọỎỏỐố
+ỒồỔổỖỗỘộỚớỜờỞởỠỡỢợỤụỦủỨứỪừỬửỮữỰựỲỳỴỵỶỷỸỹἀἁἂἃἄἅἆἇἈἉἊἋἌἍἎἏἐἑἒἓἔἕἘἙἚἛ
+ἜἝἠἡἢἣἤἥἦἧἨἩἪἫἬἭἮἯἰἱἲἳἴἵἶἷἸἹἺἻἼἽἾἿὀὁὂὃὄὅὈὉὊὋὌὍὐὑὒὓὔὕὖὗὙὛὝὟὠὡὢὣὤὥὦὧ
+ὨὩὪὫὬὭὮὯὰάὲέὴήὶίὸόὺύὼώᾀᾁᾂᾃᾄᾅᾆᾇᾐᾑᾒᾓᾔᾕᾖᾗᾠᾡᾢᾣᾤᾥᾦᾧᾰᾱᾲᾳᾴᾶᾷᾸᾹᾺΆιῂῃῄῆῇῈΈῊ
+ΉῐῑῒΐῖῗῘῙῚΊῠῡῢΰῤῥῦῧῨῩῪΎῬῲῳῴῶῷῸΌῺΏⁱⁿℂℇℊℋℌℍℎℏℐℑℒℓℕℙℚℛℜℝℤΩℨKÅℬℭℯℰℱℳℴℹ
+ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyzªµºÀÁÂÃÄÅÆÇÈÉÊ
+ËÌÍÎÏÐÑÒÓÔÕÖØÙÚÛÜÝÞßàáâãäåæçèéêëìíîïðñòóôõöøùúûüýþÿĀāĂ㥹ĆćĈĉĊċČčĎ
+ďĐđĒēĔĕĖėĘęĚěĜĝĞğĠġĢģĤĥĦħĨĩĪīĬĭĮįİıIJijĴĵĶķĸĹĺĻļĽľĿŀŁłŃńŅņŇňʼnŊŋŌōŎŏŐ
+őŒœŔŕŖŗŘřŚśŜŝŞşŠšŢţŤťŦŧŨũŪūŬŭŮůŰűŲųŴŵŶŷŸŹźŻżŽžſƀƁƂƃƄƅƆƇƈƉƊƋƌƍƎƏƐƑƒ
+ƓƔƕƖƗƘƙƚƛƜƝƞƟƠơƢƣƤƥƦƧƨƩƪƫƬƭƮƯưƱƲƳƴƵƶƷƸƹƺƼƽƾƿDŽdžLJljNJnjǍǎǏǐǑǒǓǔǕǖǗǘǙǚǛǜ
+ǝǞǟǠǡǢǣǤǥǦǧǨǩǪǫǬǭǮǯǰDZdzǴǵǶǷǸǹǺǻǼǽǾǿȀȁȂȃȄȅȆȇȈȉȊȋȌȍȎȏȐȑȒȓȔȕȖȗȘșȚțȜȝȞȟ
+ȠȡȢȣȤȥȦȧȨȩȪȫȬȭȮȯȰȱȲȳȴȵȶȷȸȹȺȻȼȽȾȿɀɁɐɑɒɓɔɕɖɗɘəɚɛɜɝɞɟɠɡɢɣɤɥɦɧɨɩɪɫɬɭɮɯ
+ɰɱɲɳɴɵɶɷɸɹɺɻɼɽɾɿʀʁʂʃʄʅʆʇʈʉʊʋʌʍʎʏʐʑʒʓʔʕʖʗʘʙʚʛʜʝʞʟʠʡʢʣʤʥʦʧʨʩʪʫʬʭʮʯΆΈ
+ΉΊΌΎΏΐΑΒΓΔΕΖΗΘΙΚΛΜΝΞΟΠΡΣΤΥΦΧΨΩΪΫάέήίΰαβγδεζηθικλμνξοπρςστυφχψωϊϋόύ
+ώϐϑϒϓϔϕϖϗϘϙϚϛϜϝϞϟϠϡϢϣϤϥϦϧϨϩϪϫϬϭϮϯϰϱϲϳϴϵϷϸϹϺϻϼϽϾϿЀЁЂЃЄЅІЇЈЉЊЋЌЍЎЏАБ
+ВГДЕЖЗИЙКЛМНОПРСТУФХЦЧШЩЪЫЬЭЮЯабвгдежзийклмнопрстуфхцчшщъыьэюяѐёђѓ
+єѕіїјљњћќѝўџѠѡѢѣѤѥѦѧѨѩѪѫѬѭѮѯѰѱѲѳѴѵѶѷѸѹѺѻѼѽѾѿҀҁҊҋҌҍҎҏҐґҒғҔҕҖҗҘҙҚқҜҝ
+ҞҟҠҡҢңҤҥҦҧҨҩҪҫҬҭҮүҰұҲҳҴҵҶҷҸҹҺһҼҽҾҿӀӁӂӃӄӅӆӇӈӉӊӋӌӍӎӐӑӒӓӔӕӖӗӘәӚӛӜӝӞӟӠ
+ӡӢӣӤӥӦӧӨөӪӫӬӭӮӯӰӱӲӳӴӵӶӷӸӹԀԁԂԃԄԅԆԇԈԉԊԋԌԍԎԏԱԲԳԴԵԶԷԸԹԺԻԼԽԾԿՀՁՂՃՄՅՆՇՈՉ
+ՊՋՌՍՎՏՐՑՒՓՔՕՖաբգդեզէըթժիլխծկհձղճմյնշոչպջռսվտրցւփքօֆևႠႡႢႣႤႥႦႧႨႩႪႫႬႭ
+ႮႯႰႱႲႳႴႵႶႷႸႹႺႻႼႽႾႿჀჁჂჃჄჅᴀᴁᴂᴃᴄᴅᴆᴇᴈᴉᴊᴋᴌᴍᴎᴏᴐᴑᴒᴓᴔᴕᴖᴗᴘᴙᴚᴛᴜᴝᴞᴟᴠᴡᴢᴣᴤᴥᴦᴧᴨᴩ
+ᴪᴫᵢᵣᵤᵥᵦᵧᵨᵩᵪᵫᵬᵭᵮᵯᵰᵱᵲᵳᵴᵵᵶᵷᵹᵺᵻᵼᵽᵾᵿᶀᶁᶂᶃᶄᶅᶆᶇᶈᶉᶊᶋᶌᶍᶎᶏᶐᶑᶒᶓᶔᶕᶖᶗᶘᶙᶚḀḁḂḃḄḅḆḇ
+ḈḉḊḋḌḍḎḏḐḑḒḓḔḕḖḗḘḙḚḛḜḝḞḟḠḡḢḣḤḥḦḧḨḩḪḫḬḭḮḯḰḱḲḳḴḵḶḷḸḹḺḻḼḽḾḿṀṁṂṃṄṅṆṇṈṉ
+ṊṋṌṍṎṏṐṑṒṓṔṕṖṗṘṙṚṛṜṝṞṟṠṡṢṣṤṥṦṧṨṩṪṫṬṭṮṯṰṱṲṳṴṵṶṷṸṹṺṻṼṽṾṿẀẁẂẃẄẅẆẇẈẉẊẋ
+ẌẍẎẏẐẑẒẓẔẕẖẗẘẙẚẛẠạẢảẤấẦầẨẩẪẫẬậẮắẰằẲẳẴẵẶặẸẹẺẻẼẽẾếỀềỂểỄễỆệỈỉỊịỌọỎỏỐố
+ỒồỔổỖỗỘộỚớỜờỞởỠỡỢợỤụỦủỨứỪừỬửỮữỰựỲỳỴỵỶỷỸỹἀἁἂἃἄἅἆἇἈἉἊἋἌἍἎἏἐἑἒἓἔἕἘἙἚἛ
+ἜἝἠἡἢἣἤἥἦἧἨἩἪἫἬἭἮἯἰἱἲἳἴἵἶἷἸἹἺἻἼἽἾἿὀὁὂὃὄὅὈὉὊὋὌὍὐὑὒὓὔὕὖὗὙὛὝὟὠὡὢὣὤὥὦὧ
+ὨὩὪὫὬὭὮὯὰάὲέὴήὶίὸόὺύὼώᾀᾁᾂᾃᾄᾅᾆᾇᾐᾑᾒᾓᾔᾕᾖᾗᾠᾡᾢᾣᾤᾥᾦᾧᾰᾱᾲᾳᾴᾶᾷᾸᾹᾺΆιῂῃῄῆῇῈΈῊ
+ΉῐῑῒΐῖῗῘῙῚΊῠῡῢΰῤῥῦῧῨῩῪΎῬῲῳῴῶῷῸΌῺΏⁱⁿℂℇℊℋℌℍℎℏℐℑℒℓℕℙℚℛℜℝℤΩℨKÅℬℭℯℰℱℳℴℹ
+ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyzªµºÀÁÂÃÄÅÆÇÈÉÊ
+ËÌÍÎÏÐÑÒÓÔÕÖØÙÚÛÜÝÞßàáâãäåæçèéêëìíîïðñòóôõöøùúûüýþÿĀāĂ㥹ĆćĈĉĊċČčĎ
+ďĐđĒēĔĕĖėĘęĚěĜĝĞğĠġĢģĤĥĦħĨĩĪīĬĭĮįİıIJijĴĵĶķĸĹĺĻļĽľĿŀŁłŃńŅņŇňʼnŊŋŌōŎŏŐ
+őŒœŔŕŖŗŘřŚśŜŝŞşŠšŢţŤťŦŧŨũŪūŬŭŮůŰűŲųŴŵŶŷŸŹźŻżŽžſƀƁƂƃƄƅƆƇƈƉƊƋƌƍƎƏƐƑƒ
+ƓƔƕƖƗƘƙƚƛƜƝƞƟƠơƢƣƤƥƦƧƨƩƪƫƬƭƮƯưƱƲƳƴƵƶƷƸƹƺƼƽƾƿDŽdžLJljNJnjǍǎǏǐǑǒǓǔǕǖǗǘǙǚǛǜ
+ǝǞǟǠǡǢǣǤǥǦǧǨǩǪǫǬǭǮǯǰDZdzǴǵǶǷǸǹǺǻǼǽǾǿȀȁȂȃȄȅȆȇȈȉȊȋȌȍȎȏȐȑȒȓȔȕȖȗȘșȚțȜȝȞȟ
+ȠȡȢȣȤȥȦȧȨȩȪȫȬȭȮȯȰȱȲȳȴȵȶȷȸȹȺȻȼȽȾȿɀɁɐɑɒɓɔɕɖɗɘəɚɛɜɝɞɟɠɡɢɣɤɥɦɧɨɩɪɫɬɭɮɯ
+ɰɱɲɳɴɵɶɷɸɹɺɻɼɽɾɿʀʁʂʃʄʅʆʇʈʉʊʋʌʍʎʏʐʑʒʓʔʕʖʗʘʙʚʛʜʝʞʟʠʡʢʣʤʥʦʧʨʩʪʫʬʭʮʯΆΈ
+ΉΊΌΎΏΐΑΒΓΔΕΖΗΘΙΚΛΜΝΞΟΠΡΣΤΥΦΧΨΩΪΫάέήίΰαβγδεζηθικλμνξοπρςστυφχψωϊϋόύ
+ώϐϑϒϓϔϕϖϗϘϙϚϛϜϝϞϟϠϡϢϣϤϥϦϧϨϩϪϫϬϭϮϯϰϱϲϳϴϵϷϸϹϺϻϼϽϾϿЀЁЂЃЄЅІЇЈЉЊЋЌЍЎЏАБ
+ВГДЕЖЗИЙКЛМНОПРСТУФХЦЧШЩЪЫЬЭЮЯабвгдежзийклмнопрстуфхцчшщъыьэюяѐёђѓ
+єѕіїјљњћќѝўџѠѡѢѣѤѥѦѧѨѩѪѫѬѭѮѯѰѱѲѳѴѵѶѷѸѹѺѻѼѽѾѿҀҁҊҋҌҍҎҏҐґҒғҔҕҖҗҘҙҚқҜҝ
+ҞҟҠҡҢңҤҥҦҧҨҩҪҫҬҭҮүҰұҲҳҴҵҶҷҸҹҺһҼҽҾҿӀӁӂӃӄӅӆӇӈӉӊӋӌӍӎӐӑӒӓӔӕӖӗӘәӚӛӜӝӞӟӠ
+ӡӢӣӤӥӦӧӨөӪӫӬӭӮӯӰӱӲӳӴӵӶӷӸӹԀԁԂԃԄԅԆԇԈԉԊԋԌԍԎԏԱԲԳԴԵԶԷԸԹԺԻԼԽԾԿՀՁՂՃՄՅՆՇՈՉ
+ՊՋՌՍՎՏՐՑՒՓՔՕՖաբգդեզէըթժիլխծկհձղճմյնշոչպջռսվտրցւփքօֆևႠႡႢႣႤႥႦႧႨႩႪႫႬႭ
+ႮႯႰႱႲႳႴႵႶႷႸႹႺႻႼႽႾႿჀჁჂჃჄჅᴀᴁᴂᴃᴄᴅᴆᴇᴈᴉᴊᴋᴌᴍᴎᴏᴐᴑᴒᴓᴔᴕᴖᴗᴘᴙᴚᴛᴜᴝᴞᴟᴠᴡᴢᴣᴤᴥᴦᴧᴨᴩ
+ᴪᴫᵢᵣᵤᵥᵦᵧᵨᵩᵪᵫᵬᵭᵮᵯᵰᵱᵲᵳᵴᵵᵶᵷᵹᵺᵻᵼᵽᵾᵿᶀᶁᶂᶃᶄᶅᶆᶇᶈᶉᶊᶋᶌᶍᶎᶏᶐᶑᶒᶓᶔᶕᶖᶗᶘᶙᶚḀḁḂḃḄḅḆḇ
+ḈḉḊḋḌḍḎḏḐḑḒḓḔḕḖḗḘḙḚḛḜḝḞḟḠḡḢḣḤḥḦḧḨḩḪḫḬḭḮḯḰḱḲḳḴḵḶḷḸḹḺḻḼḽḾḿṀṁṂṃṄṅṆṇṈṉ
+ṊṋṌṍṎṏṐṑṒṓṔṕṖṗṘṙṚṛṜṝṞṟṠṡṢṣṤṥṦṧṨṩṪṫṬṭṮṯṰṱṲṳṴṵṶṷṸṹṺṻṼṽṾṿẀẁẂẃẄẅẆẇẈẉẊẋ
+ẌẍẎẏẐẑẒẓẔẕẖẗẘẙẚẛẠạẢảẤấẦầẨẩẪẫẬậẮắẰằẲẳẴẵẶặẸẹẺẻẼẽẾếỀềỂểỄễỆệỈỉỊịỌọỎỏỐố
+ỒồỔổỖỗỘộỚớỜờỞởỠỡỢợỤụỦủỨứỪừỬửỮữỰựỲỳỴỵỶỷỸỹἀἁἂἃἄἅἆἇἈἉἊἋἌἍἎἏἐἑἒἓἔἕἘἙἚἛ
+ἜἝἠἡἢἣἤἥἦἧἨἩἪἫἬἭἮἯἰἱἲἳἴἵἶἷἸἹἺἻἼἽἾἿὀὁὂὃὄὅὈὉὊὋὌὍὐὑὒὓὔὕὖὗὙὛὝὟὠὡὢὣὤὥὦὧ
+ὨὩὪὫὬὭὮὯὰάὲέὴήὶίὸόὺύὼώᾀᾁᾂᾃᾄᾅᾆᾇᾐᾑᾒᾓᾔᾕᾖᾗᾠᾡᾢᾣᾤᾥᾦᾧᾰᾱᾲᾳᾴᾶᾷᾸᾹᾺΆιῂῃῄῆῇῈΈῊ
+ΉῐῑῒΐῖῗῘῙῚΊῠῡῢΰῤῥῦῧῨῩῪΎῬῲῳῴῶῷῸΌῺΏⁱⁿℂℇℊℋℌℍℎℏℐℑℒℓℕℙℚℛℜℝℤΩℨKÅℬℭℯℰℱℳℴℹ
+ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyzªµºÀÁÂÃÄÅÆÇÈÉÊ
+ËÌÍÎÏÐÑÒÓÔÕÖØÙÚÛÜÝÞßàáâãäåæçèéêëìíîïðñòóôõöøùúûüýþÿĀāĂ㥹ĆćĈĉĊċČčĎ
+ďĐđĒēĔĕĖėĘęĚěĜĝĞğĠġĢģĤĥĦħĨĩĪīĬĭĮįİıIJijĴĵĶķĸĹĺĻļĽľĿŀŁłŃńŅņŇňʼnŊŋŌōŎŏŐ
+őŒœŔŕŖŗŘřŚśŜŝŞşŠšŢţŤťŦŧŨũŪūŬŭŮůŰűŲųŴŵŶŷŸŹźŻżŽžſƀƁƂƃƄƅƆƇƈƉƊƋƌƍƎƏƐƑƒ
+ƓƔƕƖƗƘƙƚƛƜƝƞƟƠơƢƣƤƥƦƧƨƩƪƫƬƭƮƯưƱƲƳƴƵƶƷƸƹƺƼƽƾƿDŽdžLJljNJnjǍǎǏǐǑǒǓǔǕǖǗǘǙǚǛǜ
+ǝǞǟǠǡǢǣǤǥǦǧǨǩǪǫǬǭǮǯǰDZdzǴǵǶǷǸǹǺǻǼǽǾǿȀȁȂȃȄȅȆȇȈȉȊȋȌȍȎȏȐȑȒȓȔȕȖȗȘșȚțȜȝȞȟ
+ȠȡȢȣȤȥȦȧȨȩȪȫȬȭȮȯȰȱȲȳȴȵȶȷȸȹȺȻȼȽȾȿɀɁɐɑɒɓɔɕɖɗɘəɚɛɜɝɞɟɠɡɢɣɤɥɦɧɨɩɪɫɬɭɮɯ
+ɰɱɲɳɴɵɶɷɸɹɺɻɼɽɾɿʀʁʂʃʄʅʆʇʈʉʊʋʌʍʎʏʐʑʒʓʔʕʖʗʘʙʚʛʜʝʞʟʠʡʢʣʤʥʦʧʨʩʪʫʬʭʮʯΆΈ
+ΉΊΌΎΏΐΑΒΓΔΕΖΗΘΙΚΛΜΝΞΟΠΡΣΤΥΦΧΨΩΪΫάέήίΰαβγδεζηθικλμνξοπρςστυφχψωϊϋόύ
+ώϐϑϒϓϔϕϖϗϘϙϚϛϜϝϞϟϠϡϢϣϤϥϦϧϨϩϪϫϬϭϮϯϰϱϲϳϴϵϷϸϹϺϻϼϽϾϿЀЁЂЃЄЅІЇЈЉЊЋЌЍЎЏАБ
+ВГДЕЖЗИЙКЛМНОПРСТУФХЦЧШЩЪЫЬЭЮЯабвгдежзийклмнопрстуфхцчшщъыьэюяѐёђѓ
+єѕіїјљњћќѝўџѠѡѢѣѤѥѦѧѨѩѪѫѬѭѮѯѰѱѲѳѴѵѶѷѸѹѺѻѼѽѾѿҀҁҊҋҌҍҎҏҐґҒғҔҕҖҗҘҙҚқҜҝ
+ҞҟҠҡҢңҤҥҦҧҨҩҪҫҬҭҮүҰұҲҳҴҵҶҷҸҹҺһҼҽҾҿӀӁӂӃӄӅӆӇӈӉӊӋӌӍӎӐӑӒӓӔӕӖӗӘәӚӛӜӝӞӟӠ
+ӡӢӣӤӥӦӧӨөӪӫӬӭӮӯӰӱӲӳӴӵӶӷӸӹԀԁԂԃԄԅԆԇԈԉԊԋԌԍԎԏԱԲԳԴԵԶԷԸԹԺԻԼԽԾԿՀՁՂՃՄՅՆՇՈՉ
+ՊՋՌՍՎՏՐՑՒՓՔՕՖաբգդեզէըթժիլխծկհձղճմյնշոչպջռսվտրցւփքօֆևႠႡႢႣႤႥႦႧႨႩႪႫႬႭ
+ႮႯႰႱႲႳႴႵႶႷႸႹႺႻႼႽႾႿჀჁჂჃჄჅᴀᴁᴂᴃᴄᴅᴆᴇᴈᴉᴊᴋᴌᴍᴎᴏᴐᴑᴒᴓᴔᴕᴖᴗᴘᴙᴚᴛᴜᴝᴞᴟᴠᴡᴢᴣᴤᴥᴦᴧᴨᴩ
+ᴪᴫᵢᵣᵤᵥᵦᵧᵨᵩᵪᵫᵬᵭᵮᵯᵰᵱᵲᵳᵴᵵᵶᵷᵹᵺᵻᵼᵽᵾᵿᶀᶁᶂᶃᶄᶅᶆᶇᶈᶉᶊᶋᶌᶍᶎᶏᶐᶑᶒᶓᶔᶕᶖᶗᶘᶙᶚḀḁḂḃḄḅḆḇ
+ḈḉḊḋḌḍḎḏḐḑḒḓḔḕḖḗḘḙḚḛḜḝḞḟḠḡḢḣḤḥḦḧḨḩḪḫḬḭḮḯḰḱḲḳḴḵḶḷḸḹḺḻḼḽḾḿṀṁṂṃṄṅṆṇṈṉ
+ṊṋṌṍṎṏṐṑṒṓṔṕṖṗṘṙṚṛṜṝṞṟṠṡṢṣṤṥṦṧṨṩṪṫṬṭṮṯṰṱṲṳṴṵṶṷṸṹṺṻṼṽṾṿẀẁẂẃẄẅẆẇẈẉẊẋ
+ẌẍẎẏẐẑẒẓẔẕẖẗẘẙẚẛẠạẢảẤấẦầẨẩẪẫẬậẮắẰằẲẳẴẵẶặẸẹẺẻẼẽẾếỀềỂểỄễỆệỈỉỊịỌọỎỏỐố
+ỒồỔổỖỗỘộỚớỜờỞởỠỡỢợỤụỦủỨứỪừỬửỮữỰựỲỳỴỵỶỷỸỹἀἁἂἃἄἅἆἇἈἉἊἋἌἍἎἏἐἑἒἓἔἕἘἙἚἛ
+ἜἝἠἡἢἣἤἥἦἧἨἩἪἫἬἭἮἯἰἱἲἳἴἵἶἷἸἹἺἻἼἽἾἿὀὁὂὃὄὅὈὉὊὋὌὍὐὑὒὓὔὕὖὗὙὛὝὟὠὡὢὣὤὥὦὧ
+ὨὩὪὫὬὭὮὯὰάὲέὴήὶίὸόὺύὼώᾀᾁᾂᾃᾄᾅᾆᾇᾐᾑᾒᾓᾔᾕᾖᾗᾠᾡᾢᾣᾤᾥᾦᾧᾰᾱᾲᾳᾴᾶᾷᾸᾹᾺΆιῂῃῄῆῇῈΈῊ
+ΉῐῑῒΐῖῗῘῙῚΊῠῡῢΰῤῥῦῧῨῩῪΎῬῲῳῴῶῷῸΌῺΏⁱⁿℂℇℊℋℌℍℎℏℐℑℒℓℕℙℚℛℜℝℤΩℨKÅℬℭℯℰℱℳℴℹ
+ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyzªµºÀÁÂÃÄÅÆÇÈÉÊ
+ËÌÍÎÏÐÑÒÓÔÕÖØÙÚÛÜÝÞßàáâãäåæçèéêëìíîïðñòóôõöøùúûüýþÿĀāĂ㥹ĆćĈĉĊċČčĎ
+ďĐđĒēĔĕĖėĘęĚěĜĝĞğĠġĢģĤĥĦħĨĩĪīĬĭĮįİıIJijĴĵĶķĸĹĺĻļĽľĿŀŁłŃńŅņŇňʼnŊŋŌōŎŏŐ
+őŒœŔŕŖŗŘřŚśŜŝŞşŠšŢţŤťŦŧŨũŪūŬŭŮůŰűŲųŴŵŶŷŸŹźŻżŽžſƀƁƂƃƄƅƆƇƈƉƊƋƌƍƎƏƐƑƒ
+ƓƔƕƖƗƘƙƚƛƜƝƞƟƠơƢƣƤƥƦƧƨƩƪƫƬƭƮƯưƱƲƳƴƵƶƷƸƹƺƼƽƾƿDŽdžLJljNJnjǍǎǏǐǑǒǓǔǕǖǗǘǙǚǛǜ
+ǝǞǟǠǡǢǣǤǥǦǧǨǩǪǫǬǭǮǯǰDZdzǴǵǶǷǸǹǺǻǼǽǾǿȀȁȂȃȄȅȆȇȈȉȊȋȌȍȎȏȐȑȒȓȔȕȖȗȘșȚțȜȝȞȟ
+ȠȡȢȣȤȥȦȧȨȩȪȫȬȭȮȯȰȱȲȳȴȵȶȷȸȹȺȻȼȽȾȿɀɁɐɑɒɓɔɕɖɗɘəɚɛɜɝɞɟɠɡɢɣɤɥɦɧɨɩɪɫɬɭɮɯ
+ɰɱɲɳɴɵɶɷɸɹɺɻɼɽɾɿʀʁʂʃʄʅʆʇʈʉʊʋʌʍʎʏʐʑʒʓʔʕʖʗʘʙʚʛʜʝʞʟʠʡʢʣʤʥʦʧʨʩʪʫʬʭʮʯΆΈ
+ΉΊΌΎΏΐΑΒΓΔΕΖΗΘΙΚΛΜΝΞΟΠΡΣΤΥΦΧΨΩΪΫάέήίΰαβγδεζηθικλμνξοπρςστυφχψωϊϋόύ
+ώϐϑϒϓϔϕϖϗϘϙϚϛϜϝϞϟϠϡϢϣϤϥϦϧϨϩϪϫϬϭϮϯϰϱϲϳϴϵϷϸϹϺϻϼϽϾϿЀЁЂЃЄЅІЇЈЉЊЋЌЍЎЏАБ
+ВГДЕЖЗИЙКЛМНОПРСТУФХЦЧШЩЪЫЬЭЮЯабвгдежзийклмнопрстуфхцчшщъыьэюяѐёђѓ
+єѕіїјљњћќѝўџѠѡѢѣѤѥѦѧѨѩѪѫѬѭѮѯѰѱѲѳѴѵѶѷѸѹѺѻѼѽѾѿҀҁҊҋҌҍҎҏҐґҒғҔҕҖҗҘҙҚқҜҝ
+ҞҟҠҡҢңҤҥҦҧҨҩҪҫҬҭҮүҰұҲҳҴҵҶҷҸҹҺһҼҽҾҿӀӁӂӃӄӅӆӇӈӉӊӋӌӍӎӐӑӒӓӔӕӖӗӘәӚӛӜӝӞӟӠ
+ӡӢӣӤӥӦӧӨөӪӫӬӭӮӯӰӱӲӳӴӵӶӷӸӹԀԁԂԃԄԅԆԇԈԉԊԋԌԍԎԏԱԲԳԴԵԶԷԸԹԺԻԼԽԾԿՀՁՂՃՄՅՆՇՈՉ
+ՊՋՌՍՎՏՐՑՒՓՔՕՖաբգդեզէըթժիլխծկհձղճմյնշոչպջռսվտրցւփքօֆևႠႡႢႣႤႥႦႧႨႩႪႫႬႭ
+ႮႯႰႱႲႳႴႵႶႷႸႹႺႻႼႽႾႿჀჁჂჃჄჅᴀᴁᴂᴃᴄᴅᴆᴇᴈᴉᴊᴋᴌᴍᴎᴏᴐᴑᴒᴓᴔᴕᴖᴗᴘᴙᴚᴛᴜᴝᴞᴟᴠᴡᴢᴣᴤᴥᴦᴧᴨᴩ
+ᴪᴫᵢᵣᵤᵥᵦᵧᵨᵩᵪᵫᵬᵭᵮᵯᵰᵱᵲᵳᵴᵵᵶᵷᵹᵺᵻᵼᵽᵾᵿᶀᶁᶂᶃᶄᶅᶆᶇᶈᶉᶊᶋᶌᶍᶎᶏᶐᶑᶒᶓᶔᶕᶖᶗᶘᶙᶚḀḁḂḃḄḅḆḇ
+ḈḉḊḋḌḍḎḏḐḑḒḓḔḕḖḗḘḙḚḛḜḝḞḟḠḡḢḣḤḥḦḧḨḩḪḫḬḭḮḯḰḱḲḳḴḵḶḷḸḹḺḻḼḽḾḿṀṁṂṃṄṅṆṇṈṉ
+ṊṋṌṍṎṏṐṑṒṓṔṕṖṗṘṙṚṛṜṝṞṟṠṡṢṣṤṥṦṧṨṩṪṫṬṭṮṯṰṱṲṳṴṵṶṷṸṹṺṻṼṽṾṿẀẁẂẃẄẅẆẇẈẉẊẋ
+ẌẍẎẏẐẑẒẓẔẕẖẗẘẙẚẛẠạẢảẤấẦầẨẩẪẫẬậẮắẰằẲẳẴẵẶặẸẹẺẻẼẽẾếỀềỂểỄễỆệỈỉỊịỌọỎỏỐố
+ỒồỔổỖỗỘộỚớỜờỞởỠỡỢợỤụỦủỨứỪừỬửỮữỰựỲỳỴỵỶỷỸỹἀἁἂἃἄἅἆἇἈἉἊἋἌἍἎἏἐἑἒἓἔἕἘἙἚἛ
+ἜἝἠἡἢἣἤἥἦἧἨἩἪἫἬἭἮἯἰἱἲἳἴἵἶἷἸἹἺἻἼἽἾἿὀὁὂὃὄὅὈὉὊὋὌὍὐὑὒὓὔὕὖὗὙὛὝὟὠὡὢὣὤὥὦὧ
+ὨὩὪὫὬὭὮὯὰάὲέὴήὶίὸόὺύὼώᾀᾁᾂᾃᾄᾅᾆᾇᾐᾑᾒᾓᾔᾕᾖᾗᾠᾡᾢᾣᾤᾥᾦᾧᾰᾱᾲᾳᾴᾶᾷᾸᾹᾺΆιῂῃῄῆῇῈΈῊ
+ΉῐῑῒΐῖῗῘῙῚΊῠῡῢΰῤῥῦῧῨῩῪΎῬῲῳῴῶῷῸΌῺΏⁱⁿℂℇℊℋℌℍℎℏℐℑℒℓℕℙℚℛℜℝℤΩℨKÅℬℭℯℰℱℳℴℹ
+ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyzªµºÀÁÂÃÄÅÆÇÈÉÊ
+ËÌÍÎÏÐÑÒÓÔÕÖØÙÚÛÜÝÞßàáâãäåæçèéêëìíîïðñòóôõöøùúûüýþÿĀāĂ㥹ĆćĈĉĊċČčĎ
+ďĐđĒēĔĕĖėĘęĚěĜĝĞğĠġĢģĤĥĦħĨĩĪīĬĭĮįİıIJijĴĵĶķĸĹĺĻļĽľĿŀŁłŃńŅņŇňʼnŊŋŌōŎŏŐ
+őŒœŔŕŖŗŘřŚśŜŝŞşŠšŢţŤťŦŧŨũŪūŬŭŮůŰűŲųŴŵŶŷŸŹźŻżŽžſƀƁƂƃƄƅƆƇƈƉƊƋƌƍƎƏƐƑƒ
+ƓƔƕƖƗƘƙƚƛƜƝƞƟƠơƢƣƤƥƦƧƨƩƪƫƬƭƮƯưƱƲƳƴƵƶƷƸƹƺƼƽƾƿDŽdžLJljNJnjǍǎǏǐǑǒǓǔǕǖǗǘǙǚǛǜ
+ǝǞǟǠǡǢǣǤǥǦǧǨǩǪǫǬǭǮǯǰDZdzǴǵǶǷǸǹǺǻǼǽǾǿȀȁȂȃȄȅȆȇȈȉȊȋȌȍȎȏȐȑȒȓȔȕȖȗȘșȚțȜȝȞȟ
+ȠȡȢȣȤȥȦȧȨȩȪȫȬȭȮȯȰȱȲȳȴȵȶȷȸȹȺȻȼȽȾȿɀɁɐɑɒɓɔɕɖɗɘəɚɛɜɝɞɟɠɡɢɣɤɥɦɧɨɩɪɫɬɭɮɯ
+ɰɱɲɳɴɵɶɷɸɹɺɻɼɽɾɿʀʁʂʃʄʅʆʇʈʉʊʋʌʍʎʏʐʑʒʓʔʕʖʗʘʙʚʛʜʝʞʟʠʡʢʣʤʥʦʧʨʩʪʫʬʭʮʯΆΈ
+ΉΊΌΎΏΐΑΒΓΔΕΖΗΘΙΚΛΜΝΞΟΠΡΣΤΥΦΧΨΩΪΫάέήίΰαβγδεζηθικλμνξοπρςστυφχψωϊϋόύ
+ώϐϑϒϓϔϕϖϗϘϙϚϛϜϝϞϟϠϡϢϣϤϥϦϧϨϩϪϫϬϭϮϯϰϱϲϳϴϵϷϸϹϺϻϼϽϾϿЀЁЂЃЄЅІЇЈЉЊЋЌЍЎЏАБ
+ВГДЕЖЗИЙКЛМНОПРСТУФХЦЧШЩЪЫЬЭЮЯабвгдежзийклмнопрстуфхцчшщъыьэюяѐёђѓ
+єѕіїјљњћќѝўџѠѡѢѣѤѥѦѧѨѩѪѫѬѭѮѯѰѱѲѳѴѵѶѷѸѹѺѻѼѽѾѿҀҁҊҋҌҍҎҏҐґҒғҔҕҖҗҘҙҚқҜҝ
+ҞҟҠҡҢңҤҥҦҧҨҩҪҫҬҭҮүҰұҲҳҴҵҶҷҸҹҺһҼҽҾҿӀӁӂӃӄӅӆӇӈӉӊӋӌӍӎӐӑӒӓӔӕӖӗӘәӚӛӜӝӞӟӠ
+ӡӢӣӤӥӦӧӨөӪӫӬӭӮӯӰӱӲӳӴӵӶӷӸӹԀԁԂԃԄԅԆԇԈԉԊԋԌԍԎԏԱԲԳԴԵԶԷԸԹԺԻԼԽԾԿՀՁՂՃՄՅՆՇՈՉ
+ՊՋՌՍՎՏՐՑՒՓՔՕՖաբգդեզէըթժիլխծկհձղճմյնշոչպջռսվտրցւփքօֆևႠႡႢႣႤႥႦႧႨႩႪႫႬႭ
+ႮႯႰႱႲႳႴႵႶႷႸႹႺႻႼႽႾႿჀჁჂჃჄჅᴀᴁᴂᴃᴄᴅᴆᴇᴈᴉᴊᴋᴌᴍᴎᴏᴐᴑᴒᴓᴔᴕᴖᴗᴘᴙᴚᴛᴜᴝᴞᴟᴠᴡᴢᴣᴤᴥᴦᴧᴨᴩ
+ᴪᴫᵢᵣᵤᵥᵦᵧᵨᵩᵪᵫᵬᵭᵮᵯᵰᵱᵲᵳᵴᵵᵶᵷᵹᵺᵻᵼᵽᵾᵿᶀᶁᶂᶃᶄᶅᶆᶇᶈᶉᶊᶋᶌᶍᶎᶏᶐᶑᶒᶓᶔᶕᶖᶗᶘᶙᶚḀḁḂḃḄḅḆḇ
+ḈḉḊḋḌḍḎḏḐḑḒḓḔḕḖḗḘḙḚḛḜḝḞḟḠḡḢḣḤḥḦḧḨḩḪḫḬḭḮḯḰḱḲḳḴḵḶḷḸḹḺḻḼḽḾḿṀṁṂṃṄṅṆṇṈṉ
+ṊṋṌṍṎṏṐṑṒṓṔṕṖṗṘṙṚṛṜṝṞṟṠṡṢṣṤṥṦṧṨṩṪṫṬṭṮṯṰṱṲṳṴṵṶṷṸṹṺṻṼṽṾṿẀẁẂẃẄẅẆẇẈẉẊẋ
+ẌẍẎẏẐẑẒẓẔẕẖẗẘẙẚẛẠạẢảẤấẦầẨẩẪẫẬậẮắẰằẲẳẴẵẶặẸẹẺẻẼẽẾếỀềỂểỄễỆệỈỉỊịỌọỎỏỐố
+ỒồỔổỖỗỘộỚớỜờỞởỠỡỢợỤụỦủỨứỪừỬửỮữỰựỲỳỴỵỶỷỸỹἀἁἂἃἄἅἆἇἈἉἊἋἌἍἎἏἐἑἒἓἔἕἘἙἚἛ
+ἜἝἠἡἢἣἤἥἦἧἨἩἪἫἬἭἮἯἰἱἲἳἴἵἶἷἸἹἺἻἼἽἾἿὀὁὂὃὄὅὈὉὊὋὌὍὐὑὒὓὔὕὖὗὙὛὝὟὠὡὢὣὤὥὦὧ
+ὨὩὪὫὬὭὮὯὰάὲέὴήὶίὸόὺύὼώᾀᾁᾂᾃᾄᾅᾆᾇᾐᾑᾒᾓᾔᾕᾖᾗᾠᾡᾢᾣᾤᾥᾦᾧᾰᾱᾲᾳᾴᾶᾷᾸᾹᾺΆιῂῃῄῆῇῈΈῊ
+ΉῐῑῒΐῖῗῘῙῚΊῠῡῢΰῤῥῦῧῨῩῪΎῬῲῳῴῶῷῸΌῺΏⁱⁿℂℇℊℋℌℍℎℏℐℑℒℓℕℙℚℛℜℝℤΩℨKÅℬℭℯℰℱℳℴℹ
+ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyzªµºÀÁÂÃÄÅÆÇÈÉÊ
+ËÌÍÎÏÐÑÒÓÔÕÖØÙÚÛÜÝÞßàáâãäåæçèéêëìíîïðñòóôõöøùúûüýþÿĀāĂ㥹ĆćĈĉĊċČčĎ
+ďĐđĒēĔĕĖėĘęĚěĜĝĞğĠġĢģĤĥĦħĨĩĪīĬĭĮįİıIJijĴĵĶķĸĹĺĻļĽľĿŀŁłŃńŅņŇňʼnŊŋŌōŎŏŐ
+őŒœŔŕŖŗŘřŚśŜŝŞşŠšŢţŤťŦŧŨũŪūŬŭŮůŰűŲųŴŵŶŷŸŹźŻżŽžſƀƁƂƃƄƅƆƇƈƉƊƋƌƍƎƏƐƑƒ
+ƓƔƕƖƗƘƙƚƛƜƝƞƟƠơƢƣƤƥƦƧƨƩƪƫƬƭƮƯưƱƲƳƴƵƶƷƸƹƺƼƽƾƿDŽdžLJljNJnjǍǎǏǐǑǒǓǔǕǖǗǘǙǚǛǜ
+ǝǞǟǠǡǢǣǤǥǦǧǨǩǪǫǬǭǮǯǰDZdzǴǵǶǷǸǹǺǻǼǽǾǿȀȁȂȃȄȅȆȇȈȉȊȋȌȍȎȏȐȑȒȓȔȕȖȗȘșȚțȜȝȞȟ
+ȠȡȢȣȤȥȦȧȨȩȪȫȬȭȮȯȰȱȲȳȴȵȶȷȸȹȺȻȼȽȾȿɀɁɐɑɒɓɔɕɖɗɘəɚɛɜɝɞɟɠɡɢɣɤɥɦɧɨɩɪɫɬɭɮɯ
+ɰɱɲɳɴɵɶɷɸɹɺɻɼɽɾɿʀʁʂʃʄʅʆʇʈʉʊʋʌʍʎʏʐʑʒʓʔʕʖʗʘʙʚʛʜʝʞʟʠʡʢʣʤʥʦʧʨʩʪʫʬʭʮʯΆΈ
+ΉΊΌΎΏΐΑΒΓΔΕΖΗΘΙΚΛΜΝΞΟΠΡΣΤΥΦΧΨΩΪΫάέήίΰαβγδεζηθικλμνξοπρςστυφχψωϊϋόύ
+ώϐϑϒϓϔϕϖϗϘϙϚϛϜϝϞϟϠϡϢϣϤϥϦϧϨϩϪϫϬϭϮϯϰϱϲϳϴϵϷϸϹϺϻϼϽϾϿЀЁЂЃЄЅІЇЈЉЊЋЌЍЎЏАБ
+ВГДЕЖЗИЙКЛМНОПРСТУФХЦЧШЩЪЫЬЭЮЯабвгдежзийклмнопрстуфхцчшщъыьэюяѐёђѓ
+єѕіїјљњћќѝўџѠѡѢѣѤѥѦѧѨѩѪѫѬѭѮѯѰѱѲѳѴѵѶѷѸѹѺѻѼѽѾѿҀҁҊҋҌҍҎҏҐґҒғҔҕҖҗҘҙҚқҜҝ
+ҞҟҠҡҢңҤҥҦҧҨҩҪҫҬҭҮүҰұҲҳҴҵҶҷҸҹҺһҼҽҾҿӀӁӂӃӄӅӆӇӈӉӊӋӌӍӎӐӑӒӓӔӕӖӗӘәӚӛӜӝӞӟӠ
+ӡӢӣӤӥӦӧӨөӪӫӬӭӮӯӰӱӲӳӴӵӶӷӸӹԀԁԂԃԄԅԆԇԈԉԊԋԌԍԎԏԱԲԳԴԵԶԷԸԹԺԻԼԽԾԿՀՁՂՃՄՅՆՇՈՉ
+ՊՋՌՍՎՏՐՑՒՓՔՕՖաբգդեզէըթժիլխծկհձղճմյնշոչպջռսվտրցւփքօֆևႠႡႢႣႤႥႦႧႨႩႪႫႬႭ
+ႮႯႰႱႲႳႴႵႶႷႸႹႺႻႼႽႾႿჀჁჂჃჄჅᴀᴁᴂᴃᴄᴅᴆᴇᴈᴉᴊᴋᴌᴍᴎᴏᴐᴑᴒᴓᴔᴕᴖᴗᴘᴙᴚᴛᴜᴝᴞᴟᴠᴡᴢᴣᴤᴥᴦᴧᴨᴩ
+ᴪᴫᵢᵣᵤᵥᵦᵧᵨᵩᵪᵫᵬᵭᵮᵯᵰᵱᵲᵳᵴᵵᵶᵷᵹᵺᵻᵼᵽᵾᵿᶀᶁᶂᶃᶄᶅᶆᶇᶈᶉᶊᶋᶌᶍᶎᶏᶐᶑᶒᶓᶔᶕᶖᶗᶘᶙᶚḀḁḂḃḄḅḆḇ
+ḈḉḊḋḌḍḎḏḐḑḒḓḔḕḖḗḘḙḚḛḜḝḞḟḠḡḢḣḤḥḦḧḨḩḪḫḬḭḮḯḰḱḲḳḴḵḶḷḸḹḺḻḼḽḾḿṀṁṂṃṄṅṆṇṈṉ
+ṊṋṌṍṎṏṐṑṒṓṔṕṖṗṘṙṚṛṜṝṞṟṠṡṢṣṤṥṦṧṨṩṪṫṬṭṮṯṰṱṲṳṴṵṶṷṸṹṺṻṼṽṾṿẀẁẂẃẄẅẆẇẈẉẊẋ
+ẌẍẎẏẐẑẒẓẔẕẖẗẘẙẚẛẠạẢảẤấẦầẨẩẪẫẬậẮắẰằẲẳẴẵẶặẸẹẺẻẼẽẾếỀềỂểỄễỆệỈỉỊịỌọỎỏỐố
+ỒồỔổỖỗỘộỚớỜờỞởỠỡỢợỤụỦủỨứỪừỬửỮữỰựỲỳỴỵỶỷỸỹἀἁἂἃἄἅἆἇἈἉἊἋἌἍἎἏἐἑἒἓἔἕἘἙἚἛ
+ἜἝἠἡἢἣἤἥἦἧἨἩἪἫἬἭἮἯἰἱἲἳἴἵἶἷἸἹἺἻἼἽἾἿὀὁὂὃὄὅὈὉὊὋὌὍὐὑὒὓὔὕὖὗὙὛὝὟὠὡὢὣὤὥὦὧ
+ὨὩὪὫὬὭὮὯὰάὲέὴήὶίὸόὺύὼώᾀᾁᾂᾃᾄᾅᾆᾇᾐᾑᾒᾓᾔᾕᾖᾗᾠᾡᾢᾣᾤᾥᾦᾧᾰᾱᾲᾳᾴᾶᾷᾸᾹᾺΆιῂῃῄῆῇῈΈῊ
+ΉῐῑῒΐῖῗῘῙῚΊῠῡῢΰῤῥῦῧῨῩῪΎῬῲῳῴῶῷῸΌῺΏⁱⁿℂℇℊℋℌℍℎℏℐℑℒℓℕℙℚℛℜℝℤΩℨKÅℬℭℯℰℱℳℴℹ
+ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyzªµºÀÁÂÃÄÅÆÇÈÉÊ
+ËÌÍÎÏÐÑÒÓÔÕÖØÙÚÛÜÝÞßàáâãäåæçèéêëìíîïðñòóôõöøùúûüýþÿĀāĂ㥹ĆćĈĉĊċČčĎ
+ďĐđĒēĔĕĖėĘęĚěĜĝĞğĠġĢģĤĥĦħĨĩĪīĬĭĮįİıIJijĴĵĶķĸĹĺĻļĽľĿŀŁłŃńŅņŇňʼnŊŋŌōŎŏŐ
+őŒœŔŕŖŗŘřŚśŜŝŞşŠšŢţŤťŦŧŨũŪūŬŭŮůŰűŲųŴŵŶŷŸŹźŻżŽžſƀƁƂƃƄƅƆƇƈƉƊƋƌƍƎƏƐƑƒ
+ƓƔƕƖƗƘƙƚƛƜƝƞƟƠơƢƣƤƥƦƧƨƩƪƫƬƭƮƯưƱƲƳƴƵƶƷƸƹƺƼƽƾƿDŽdžLJljNJnjǍǎǏǐǑǒǓǔǕǖǗǘǙǚǛǜ
+ǝǞǟǠǡǢǣǤǥǦǧǨǩǪǫǬǭǮǯǰDZdzǴǵǶǷǸǹǺǻǼǽǾǿȀȁȂȃȄȅȆȇȈȉȊȋȌȍȎȏȐȑȒȓȔȕȖȗȘșȚțȜȝȞȟ
+ȠȡȢȣȤȥȦȧȨȩȪȫȬȭȮȯȰȱȲȳȴȵȶȷȸȹȺȻȼȽȾȿɀɁɐɑɒɓɔɕɖɗɘəɚɛɜɝɞɟɠɡɢɣɤɥɦɧɨɩɪɫɬɭɮɯ
+ɰɱɲɳɴɵɶɷɸɹɺɻɼɽɾɿʀʁʂʃʄʅʆʇʈʉʊʋʌʍʎʏʐʑʒʓʔʕʖʗʘʙʚʛʜʝʞʟʠʡʢʣʤʥʦʧʨʩʪʫʬʭʮʯΆΈ
+ΉΊΌΎΏΐΑΒΓΔΕΖΗΘΙΚΛΜΝΞΟΠΡΣΤΥΦΧΨΩΪΫάέήίΰαβγδεζηθικλμνξοπρςστυφχψωϊϋόύ
+ώϐϑϒϓϔϕϖϗϘϙϚϛϜϝϞϟϠϡϢϣϤϥϦϧϨϩϪϫϬϭϮϯϰϱϲϳϴϵϷϸϹϺϻϼϽϾϿЀЁЂЃЄЅІЇЈЉЊЋЌЍЎЏАБ
+ВГДЕЖЗИЙКЛМНОПРСТУФХЦЧШЩЪЫЬЭЮЯабвгдежзийклмнопрстуфхцчшщъыьэюяѐёђѓ
+єѕіїјљњћќѝўџѠѡѢѣѤѥѦѧѨѩѪѫѬѭѮѯѰѱѲѳѴѵѶѷѸѹѺѻѼѽѾѿҀҁҊҋҌҍҎҏҐґҒғҔҕҖҗҘҙҚқҜҝ
+ҞҟҠҡҢңҤҥҦҧҨҩҪҫҬҭҮүҰұҲҳҴҵҶҷҸҹҺһҼҽҾҿӀӁӂӃӄӅӆӇӈӉӊӋӌӍӎӐӑӒӓӔӕӖӗӘәӚӛӜӝӞӟӠ
+ӡӢӣӤӥӦӧӨөӪӫӬӭӮӯӰӱӲӳӴӵӶӷӸӹԀԁԂԃԄԅԆԇԈԉԊԋԌԍԎԏԱԲԳԴԵԶԷԸԹԺԻԼԽԾԿՀՁՂՃՄՅՆՇՈՉ
+ՊՋՌՍՎՏՐՑՒՓՔՕՖաբգդեզէըթժիլխծկհձղճմյնշոչպջռսվտրցւփքօֆևႠႡႢႣႤႥႦႧႨႩႪႫႬႭ
+ႮႯႰႱႲႳႴႵႶႷႸႹႺႻႼႽႾႿჀჁჂჃჄჅᴀᴁᴂᴃᴄᴅᴆᴇᴈᴉᴊᴋᴌᴍᴎᴏᴐᴑᴒᴓᴔᴕᴖᴗᴘᴙᴚᴛᴜᴝᴞᴟᴠᴡᴢᴣᴤᴥᴦᴧᴨᴩ
+ᴪᴫᵢᵣᵤᵥᵦᵧᵨᵩᵪᵫᵬᵭᵮᵯᵰᵱᵲᵳᵴᵵᵶᵷᵹᵺᵻᵼᵽᵾᵿᶀᶁᶂᶃᶄᶅᶆᶇᶈᶉᶊᶋᶌᶍᶎᶏᶐᶑᶒᶓᶔᶕᶖᶗᶘᶙᶚḀḁḂḃḄḅḆḇ
+ḈḉḊḋḌḍḎḏḐḑḒḓḔḕḖḗḘḙḚḛḜḝḞḟḠḡḢḣḤḥḦḧḨḩḪḫḬḭḮḯḰḱḲḳḴḵḶḷḸḹḺḻḼḽḾḿṀṁṂṃṄṅṆṇṈṉ
+ṊṋṌṍṎṏṐṑṒṓṔṕṖṗṘṙṚṛṜṝṞṟṠṡṢṣṤṥṦṧṨṩṪṫṬṭṮṯṰṱṲṳṴṵṶṷṸṹṺṻṼṽṾṿẀẁẂẃẄẅẆẇẈẉẊẋ
+ẌẍẎẏẐẑẒẓẔẕẖẗẘẙẚẛẠạẢảẤấẦầẨẩẪẫẬậẮắẰằẲẳẴẵẶặẸẹẺẻẼẽẾếỀềỂểỄễỆệỈỉỊịỌọỎỏỐố
+ỒồỔổỖỗỘộỚớỜờỞởỠỡỢợỤụỦủỨứỪừỬửỮữỰựỲỳỴỵỶỷỸỹἀἁἂἃἄἅἆἇἈἉἊἋἌἍἎἏἐἑἒἓἔἕἘἙἚἛ
+ἜἝἠἡἢἣἤἥἦἧἨἩἪἫἬἭἮἯἰἱἲἳἴἵἶἷἸἹἺἻἼἽἾἿὀὁὂὃὄὅὈὉὊὋὌὍὐὑὒὓὔὕὖὗὙὛὝὟὠὡὢὣὤὥὦὧ
+ὨὩὪὫὬὭὮὯὰάὲέὴήὶίὸόὺύὼώᾀᾁᾂᾃᾄᾅᾆᾇᾐᾑᾒᾓᾔᾕᾖᾗᾠᾡᾢᾣᾤᾥᾦᾧᾰᾱᾲᾳᾴᾶᾷᾸᾹᾺΆιῂῃῄῆῇῈΈῊ
+ΉῐῑῒΐῖῗῘῙῚΊῠῡῢΰῤῥῦῧῨῩῪΎῬῲῳῴῶῷῸΌῺΏⁱⁿℂℇℊℋℌℍℎℏℐℑℒℓℕℙℚℛℜℝℤΩℨKÅℬℭℯℰℱℳℴℹ
+ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyzªµºÀÁÂÃÄÅÆÇÈÉÊ
+ËÌÍÎÏÐÑÒÓÔÕÖØÙÚÛÜÝÞßàáâãäåæçèéêëìíîïðñòóôõöøùúûüýþÿĀāĂ㥹ĆćĈĉĊċČčĎ
+ďĐđĒēĔĕĖėĘęĚěĜĝĞğĠġĢģĤĥĦħĨĩĪīĬĭĮįİıIJijĴĵĶķĸĹĺĻļĽľĿŀŁłŃńŅņŇňʼnŊŋŌōŎŏŐ
+őŒœŔŕŖŗŘřŚśŜŝŞşŠšŢţŤťŦŧŨũŪūŬŭŮůŰűŲųŴŵŶŷŸŹźŻżŽžſƀƁƂƃƄƅƆƇƈƉƊƋƌƍƎƏƐƑƒ
+ƓƔƕƖƗƘƙƚƛƜƝƞƟƠơƢƣƤƥƦƧƨƩƪƫƬƭƮƯưƱƲƳƴƵƶƷƸƹƺƼƽƾƿDŽdžLJljNJnjǍǎǏǐǑǒǓǔǕǖǗǘǙǚǛǜ
+ǝǞǟǠǡǢǣǤǥǦǧǨǩǪǫǬǭǮǯǰDZdzǴǵǶǷǸǹǺǻǼǽǾǿȀȁȂȃȄȅȆȇȈȉȊȋȌȍȎȏȐȑȒȓȔȕȖȗȘșȚțȜȝȞȟ
+ȠȡȢȣȤȥȦȧȨȩȪȫȬȭȮȯȰȱȲȳȴȵȶȷȸȹȺȻȼȽȾȿɀɁɐɑɒɓɔɕɖɗɘəɚɛɜɝɞɟɠɡɢɣɤɥɦɧɨɩɪɫɬɭɮɯ
+ɰɱɲɳɴɵɶɷɸɹɺɻɼɽɾɿʀʁʂʃʄʅʆʇʈʉʊʋʌʍʎʏʐʑʒʓʔʕʖʗʘʙʚʛʜʝʞʟʠʡʢʣʤʥʦʧʨʩʪʫʬʭʮʯΆΈ
+ΉΊΌΎΏΐΑΒΓΔΕΖΗΘΙΚΛΜΝΞΟΠΡΣΤΥΦΧΨΩΪΫάέήίΰαβγδεζηθικλμνξοπρςστυφχψωϊϋόύ
+ώϐϑϒϓϔϕϖϗϘϙϚϛϜϝϞϟϠϡϢϣϤϥϦϧϨϩϪϫϬϭϮϯϰϱϲϳϴϵϷϸϹϺϻϼϽϾϿЀЁЂЃЄЅІЇЈЉЊЋЌЍЎЏАБ
+ВГДЕЖЗИЙКЛМНОПРСТУФХЦЧШЩЪЫЬЭЮЯабвгдежзийклмнопрстуфхцчшщъыьэюяѐёђѓ
+єѕіїјљњћќѝўџѠѡѢѣѤѥѦѧѨѩѪѫѬѭѮѯѰѱѲѳѴѵѶѷѸѹѺѻѼѽѾѿҀҁҊҋҌҍҎҏҐґҒғҔҕҖҗҘҙҚқҜҝ
+ҞҟҠҡҢңҤҥҦҧҨҩҪҫҬҭҮүҰұҲҳҴҵҶҷҸҹҺһҼҽҾҿӀӁӂӃӄӅӆӇӈӉӊӋӌӍӎӐӑӒӓӔӕӖӗӘәӚӛӜӝӞӟӠ
+ӡӢӣӤӥӦӧӨөӪӫӬӭӮӯӰӱӲӳӴӵӶӷӸӹԀԁԂԃԄԅԆԇԈԉԊԋԌԍԎԏԱԲԳԴԵԶԷԸԹԺԻԼԽԾԿՀՁՂՃՄՅՆՇՈՉ
+ՊՋՌՍՎՏՐՑՒՓՔՕՖաբգդեզէըթժիլխծկհձղճմյնշոչպջռսվտրցւփքօֆևႠႡႢႣႤႥႦႧႨႩႪႫႬႭ
+ႮႯႰႱႲႳႴႵႶႷႸႹႺႻႼႽႾႿჀჁჂჃჄჅᴀᴁᴂᴃᴄᴅᴆᴇᴈᴉᴊᴋᴌᴍᴎᴏᴐᴑᴒᴓᴔᴕᴖᴗᴘᴙᴚᴛᴜᴝᴞᴟᴠᴡᴢᴣᴤᴥᴦᴧᴨᴩ
+ᴪᴫᵢᵣᵤᵥᵦᵧᵨᵩᵪᵫᵬᵭᵮᵯᵰᵱᵲᵳᵴᵵᵶᵷᵹᵺᵻᵼᵽᵾᵿᶀᶁᶂᶃᶄᶅᶆᶇᶈᶉᶊᶋᶌᶍᶎᶏᶐᶑᶒᶓᶔᶕᶖᶗᶘᶙᶚḀḁḂḃḄḅḆḇ
+ḈḉḊḋḌḍḎḏḐḑḒḓḔḕḖḗḘḙḚḛḜḝḞḟḠḡḢḣḤḥḦḧḨḩḪḫḬḭḮḯḰḱḲḳḴḵḶḷḸḹḺḻḼḽḾḿṀṁṂṃṄṅṆṇṈṉ
+ṊṋṌṍṎṏṐṑṒṓṔṕṖṗṘṙṚṛṜṝṞṟṠṡṢṣṤṥṦṧṨṩṪṫṬṭṮṯṰṱṲṳṴṵṶṷṸṹṺṻṼṽṾṿẀẁẂẃẄẅẆẇẈẉẊẋ
+ẌẍẎẏẐẑẒẓẔẕẖẗẘẙẚẛẠạẢảẤấẦầẨẩẪẫẬậẮắẰằẲẳẴẵẶặẸẹẺẻẼẽẾếỀềỂểỄễỆệỈỉỊịỌọỎỏỐố
+ỒồỔổỖỗỘộỚớỜờỞởỠỡỢợỤụỦủỨứỪừỬửỮữỰựỲỳỴỵỶỷỸỹἀἁἂἃἄἅἆἇἈἉἊἋἌἍἎἏἐἑἒἓἔἕἘἙἚἛ
+ἜἝἠἡἢἣἤἥἦἧἨἩἪἫἬἭἮἯἰἱἲἳἴἵἶἷἸἹἺἻἼἽἾἿὀὁὂὃὄὅὈὉὊὋὌὍὐὑὒὓὔὕὖὗὙὛὝὟὠὡὢὣὤὥὦὧ
+ὨὩὪὫὬὭὮὯὰάὲέὴήὶίὸόὺύὼώᾀᾁᾂᾃᾄᾅᾆᾇᾐᾑᾒᾓᾔᾕᾖᾗᾠᾡᾢᾣᾤᾥᾦᾧᾰᾱᾲᾳᾴᾶᾷᾸᾹᾺΆιῂῃῄῆῇῈΈῊ
+ΉῐῑῒΐῖῗῘῙῚΊῠῡῢΰῤῥῦῧῨῩῪΎῬῲῳῴῶῷῸΌῺΏⁱⁿℂℇℊℋℌℍℎℏℐℑℒℓℕℙℚℛℜℝℤΩℨKÅℬℭℯℰℱℳℴℹ
+ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyzªµºÀÁÂÃÄÅÆÇÈÉÊ
+ËÌÍÎÏÐÑÒÓÔÕÖØÙÚÛÜÝÞßàáâãäåæçèéêëìíîïðñòóôõöøùúûüýþÿĀāĂ㥹ĆćĈĉĊċČčĎ
+ďĐđĒēĔĕĖėĘęĚěĜĝĞğĠġĢģĤĥĦħĨĩĪīĬĭĮįİıIJijĴĵĶķĸĹĺĻļĽľĿŀŁłŃńŅņŇňʼnŊŋŌōŎŏŐ
+őŒœŔŕŖŗŘřŚśŜŝŞşŠšŢţŤťŦŧŨũŪūŬŭŮůŰűŲųŴŵŶŷŸŹźŻżŽžſƀƁƂƃƄƅƆƇƈƉƊƋƌƍƎƏƐƑƒ
+ƓƔƕƖƗƘƙƚƛƜƝƞƟƠơƢƣƤƥƦƧƨƩƪƫƬƭƮƯưƱƲƳƴƵƶƷƸƹƺƼƽƾƿDŽdžLJljNJnjǍǎǏǐǑǒǓǔǕǖǗǘǙǚǛǜ
+ǝǞǟǠǡǢǣǤǥǦǧǨǩǪǫǬǭǮǯǰDZdzǴǵǶǷǸǹǺǻǼǽǾǿȀȁȂȃȄȅȆȇȈȉȊȋȌȍȎȏȐȑȒȓȔȕȖȗȘșȚțȜȝȞȟ
+ȠȡȢȣȤȥȦȧȨȩȪȫȬȭȮȯȰȱȲȳȴȵȶȷȸȹȺȻȼȽȾȿɀɁɐɑɒɓɔɕɖɗɘəɚɛɜɝɞɟɠɡɢɣɤɥɦɧɨɩɪɫɬɭɮɯ
+ɰɱɲɳɴɵɶɷɸɹɺɻɼɽɾɿʀʁʂʃʄʅʆʇʈʉʊʋʌʍʎʏʐʑʒʓʔʕʖʗʘʙʚʛʜʝʞʟʠʡʢʣʤʥʦʧʨʩʪʫʬʭʮʯΆΈ
+ΉΊΌΎΏΐΑΒΓΔΕΖΗΘΙΚΛΜΝΞΟΠΡΣΤΥΦΧΨΩΪΫάέήίΰαβγδεζηθικλμνξοπρςστυφχψωϊϋόύ
+ώϐϑϒϓϔϕϖϗϘϙϚϛϜϝϞϟϠϡϢϣϤϥϦϧϨϩϪϫϬϭϮϯϰϱϲϳϴϵϷϸϹϺϻϼϽϾϿЀЁЂЃЄЅІЇЈЉЊЋЌЍЎЏАБ
+ВГДЕЖЗИЙКЛМНОПРСТУФХЦЧШЩЪЫЬЭЮЯабвгдежзийклмнопрстуфхцчшщъыьэюяѐёђѓ
+єѕіїјљњћќѝўџѠѡѢѣѤѥѦѧѨѩѪѫѬѭѮѯѰѱѲѳѴѵѶѷѸѹѺѻѼѽѾѿҀҁҊҋҌҍҎҏҐґҒғҔҕҖҗҘҙҚқҜҝ
+ҞҟҠҡҢңҤҥҦҧҨҩҪҫҬҭҮүҰұҲҳҴҵҶҷҸҹҺһҼҽҾҿӀӁӂӃӄӅӆӇӈӉӊӋӌӍӎӐӑӒӓӔӕӖӗӘәӚӛӜӝӞӟӠ
+ӡӢӣӤӥӦӧӨөӪӫӬӭӮӯӰӱӲӳӴӵӶӷӸӹԀԁԂԃԄԅԆԇԈԉԊԋԌԍԎԏԱԲԳԴԵԶԷԸԹԺԻԼԽԾԿՀՁՂՃՄՅՆՇՈՉ
+ՊՋՌՍՎՏՐՑՒՓՔՕՖաբգդեզէըթժիլխծկհձղճմյնշոչպջռսվտրցւփքօֆևႠႡႢႣႤႥႦႧႨႩႪႫႬႭ
+ႮႯႰႱႲႳႴႵႶႷႸႹႺႻႼႽႾႿჀჁჂჃჄჅᴀᴁᴂᴃᴄᴅᴆᴇᴈᴉᴊᴋᴌᴍᴎᴏᴐᴑᴒᴓᴔᴕᴖᴗᴘᴙᴚᴛᴜᴝᴞᴟᴠᴡᴢᴣᴤᴥᴦᴧᴨᴩ
+ᴪᴫᵢᵣᵤᵥᵦᵧᵨᵩᵪᵫᵬᵭᵮᵯᵰᵱᵲᵳᵴᵵᵶᵷᵹᵺᵻᵼᵽᵾᵿᶀᶁᶂᶃᶄᶅᶆᶇᶈᶉᶊᶋᶌᶍᶎᶏᶐᶑᶒᶓᶔᶕᶖᶗᶘᶙᶚḀḁḂḃḄḅḆḇ
+ḈḉḊḋḌḍḎḏḐḑḒḓḔḕḖḗḘḙḚḛḜḝḞḟḠḡḢḣḤḥḦḧḨḩḪḫḬḭḮḯḰḱḲḳḴḵḶḷḸḹḺḻḼḽḾḿṀṁṂṃṄṅṆṇṈṉ
+ṊṋṌṍṎṏṐṑṒṓṔṕṖṗṘṙṚṛṜṝṞṟṠṡṢṣṤṥṦṧṨṩṪṫṬṭṮṯṰṱṲṳṴṵṶṷṸṹṺṻṼṽṾṿẀẁẂẃẄẅẆẇẈẉẊẋ
+ẌẍẎẏẐẑẒẓẔẕẖẗẘẙẚẛẠạẢảẤấẦầẨẩẪẫẬậẮắẰằẲẳẴẵẶặẸẹẺẻẼẽẾếỀềỂểỄễỆệỈỉỊịỌọỎỏỐố
+ỒồỔổỖỗỘộỚớỜờỞởỠỡỢợỤụỦủỨứỪừỬửỮữỰựỲỳỴỵỶỷỸỹἀἁἂἃἄἅἆἇἈἉἊἋἌἍἎἏἐἑἒἓἔἕἘἙἚἛ
+ἜἝἠἡἢἣἤἥἦἧἨἩἪἫἬἭἮἯἰἱἲἳἴἵἶἷἸἹἺἻἼἽἾἿὀὁὂὃὄὅὈὉὊὋὌὍὐὑὒὓὔὕὖὗὙὛὝὟὠὡὢὣὤὥὦὧ
+ὨὩὪὫὬὭὮὯὰάὲέὴήὶίὸόὺύὼώᾀᾁᾂᾃᾄᾅᾆᾇᾐᾑᾒᾓᾔᾕᾖᾗᾠᾡᾢᾣᾤᾥᾦᾧᾰᾱᾲᾳᾴᾶᾷᾸᾹᾺΆιῂῃῄῆῇῈΈῊ
+ΉῐῑῒΐῖῗῘῙῚΊῠῡῢΰῤῥῦῧῨῩῪΎῬῲῳῴῶῷῸΌῺΏⁱⁿℂℇℊℋℌℍℎℏℐℑℒℓℕℙℚℛℜℝℤΩℨKÅℬℭℯℰℱℳℴℹ
+ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyzªµºÀÁÂÃÄÅÆÇÈÉÊ
+ËÌÍÎÏÐÑÒÓÔÕÖØÙÚÛÜÝÞßàáâãäåæçèéêëìíîïðñòóôõöøùúûüýþÿĀāĂ㥹ĆćĈĉĊċČčĎ
+ďĐđĒēĔĕĖėĘęĚěĜĝĞğĠġĢģĤĥĦħĨĩĪīĬĭĮįİıIJijĴĵĶķĸĹĺĻļĽľĿŀŁłŃńŅņŇňʼnŊŋŌōŎŏŐ
+őŒœŔŕŖŗŘřŚśŜŝŞşŠšŢţŤťŦŧŨũŪūŬŭŮůŰűŲųŴŵŶŷŸŹźŻżŽžſƀƁƂƃƄƅƆƇƈƉƊƋƌƍƎƏƐƑƒ
+ƓƔƕƖƗƘƙƚƛƜƝƞƟƠơƢƣƤƥƦƧƨƩƪƫƬƭƮƯưƱƲƳƴƵƶƷƸƹƺƼƽƾƿDŽdžLJljNJnjǍǎǏǐǑǒǓǔǕǖǗǘǙǚǛǜ
+ǝǞǟǠǡǢǣǤǥǦǧǨǩǪǫǬǭǮǯǰDZdzǴǵǶǷǸǹǺǻǼǽǾǿȀȁȂȃȄȅȆȇȈȉȊȋȌȍȎȏȐȑȒȓȔȕȖȗȘșȚțȜȝȞȟ
+ȠȡȢȣȤȥȦȧȨȩȪȫȬȭȮȯȰȱȲȳȴȵȶȷȸȹȺȻȼȽȾȿɀɁɐɑɒɓɔɕɖɗɘəɚɛɜɝɞɟɠɡɢɣɤɥɦɧɨɩɪɫɬɭɮɯ
+ɰɱɲɳɴɵɶɷɸɹɺɻɼɽɾɿʀʁʂʃʄʅʆʇʈʉʊʋʌʍʎʏʐʑʒʓʔʕʖʗʘʙʚʛʜʝʞʟʠʡʢʣʤʥʦʧʨʩʪʫʬʭʮʯΆΈ
+ΉΊΌΎΏΐΑΒΓΔΕΖΗΘΙΚΛΜΝΞΟΠΡΣΤΥΦΧΨΩΪΫάέήίΰαβγδεζηθικλμνξοπρςστυφχψωϊϋόύ
+ώϐϑϒϓϔϕϖϗϘϙϚϛϜϝϞϟϠϡϢϣϤϥϦϧϨϩϪϫϬϭϮϯϰϱϲϳϴϵϷϸϹϺϻϼϽϾϿЀЁЂЃЄЅІЇЈЉЊЋЌЍЎЏАБ
+ВГДЕЖЗИЙКЛМНОПРСТУФХЦЧШЩЪЫЬЭЮЯабвгдежзийклмнопрстуфхцчшщъыьэюяѐёђѓ
+єѕіїјљњћќѝўџѠѡѢѣѤѥѦѧѨѩѪѫѬѭѮѯѰѱѲѳѴѵѶѷѸѹѺѻѼѽѾѿҀҁҊҋҌҍҎҏҐґҒғҔҕҖҗҘҙҚқҜҝ
+ҞҟҠҡҢңҤҥҦҧҨҩҪҫҬҭҮүҰұҲҳҴҵҶҷҸҹҺһҼҽҾҿӀӁӂӃӄӅӆӇӈӉӊӋӌӍӎӐӑӒӓӔӕӖӗӘәӚӛӜӝӞӟӠ
+ӡӢӣӤӥӦӧӨөӪӫӬӭӮӯӰӱӲӳӴӵӶӷӸӹԀԁԂԃԄԅԆԇԈԉԊԋԌԍԎԏԱԲԳԴԵԶԷԸԹԺԻԼԽԾԿՀՁՂՃՄՅՆՇՈՉ
+ՊՋՌՍՎՏՐՑՒՓՔՕՖաբգդեզէըթժիլխծկհձղճմյնշոչպջռսվտրցւփքօֆևႠႡႢႣႤႥႦႧႨႩႪႫႬႭ
+ႮႯႰႱႲႳႴႵႶႷႸႹႺႻႼႽႾႿჀჁჂჃჄჅᴀᴁᴂᴃᴄᴅᴆᴇᴈᴉᴊᴋᴌᴍᴎᴏᴐᴑᴒᴓᴔᴕᴖᴗᴘᴙᴚᴛᴜᴝᴞᴟᴠᴡᴢᴣᴤᴥᴦᴧᴨᴩ
+ᴪᴫᵢᵣᵤᵥᵦᵧᵨᵩᵪᵫᵬᵭᵮᵯᵰᵱᵲᵳᵴᵵᵶᵷᵹᵺᵻᵼᵽᵾᵿᶀᶁᶂᶃᶄᶅᶆᶇᶈᶉᶊᶋᶌᶍᶎᶏᶐᶑᶒᶓᶔᶕᶖᶗᶘᶙᶚḀḁḂḃḄḅḆḇ
+ḈḉḊḋḌḍḎḏḐḑḒḓḔḕḖḗḘḙḚḛḜḝḞḟḠḡḢḣḤḥḦḧḨḩḪḫḬḭḮḯḰḱḲḳḴḵḶḷḸḹḺḻḼḽḾḿṀṁṂṃṄṅṆṇṈṉ
+ṊṋṌṍṎṏṐṑṒṓṔṕṖṗṘṙṚṛṜṝṞṟṠṡṢṣṤṥṦṧṨṩṪṫṬṭṮṯṰṱṲṳṴṵṶṷṸṹṺṻṼṽṾṿẀẁẂẃẄẅẆẇẈẉẊẋ
+ẌẍẎẏẐẑẒẓẔẕẖẗẘẙẚẛẠạẢảẤấẦầẨẩẪẫẬậẮắẰằẲẳẴẵẶặẸẹẺẻẼẽẾếỀềỂểỄễỆệỈỉỊịỌọỎỏỐố
+ỒồỔổỖỗỘộỚớỜờỞởỠỡỢợỤụỦủỨứỪừỬửỮữỰựỲỳỴỵỶỷỸỹἀἁἂἃἄἅἆἇἈἉἊἋἌἍἎἏἐἑἒἓἔἕἘἙἚἛ
+ἜἝἠἡἢἣἤἥἦἧἨἩἪἫἬἭἮἯἰἱἲἳἴἵἶἷἸἹἺἻἼἽἾἿὀὁὂὃὄὅὈὉὊὋὌὍὐὑὒὓὔὕὖὗὙὛὝὟὠὡὢὣὤὥὦὧ
+ὨὩὪὫὬὭὮὯὰάὲέὴήὶίὸόὺύὼώᾀᾁᾂᾃᾄᾅᾆᾇᾐᾑᾒᾓᾔᾕᾖᾗᾠᾡᾢᾣᾤᾥᾦᾧᾰᾱᾲᾳᾴᾶᾷᾸᾹᾺΆιῂῃῄῆῇῈΈῊ
+ΉῐῑῒΐῖῗῘῙῚΊῠῡῢΰῤῥῦῧῨῩῪΎῬῲῳῴῶῷῸΌῺΏⁱⁿℂℇℊℋℌℍℎℏℐℑℒℓℕℙℚℛℜℝℤΩℨKÅℬℭℯℰℱℳℴℹ
+ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyzªµºÀÁÂÃÄÅÆÇÈÉÊ
+ËÌÍÎÏÐÑÒÓÔÕÖØÙÚÛÜÝÞßàáâãäåæçèéêëìíîïðñòóôõöøùúûüýþÿĀāĂ㥹ĆćĈĉĊċČčĎ
+ďĐđĒēĔĕĖėĘęĚěĜĝĞğĠġĢģĤĥĦħĨĩĪīĬĭĮįİıIJijĴĵĶķĸĹĺĻļĽľĿŀŁłŃńŅņŇňʼnŊŋŌōŎŏŐ
+őŒœŔŕŖŗŘřŚśŜŝŞşŠšŢţŤťŦŧŨũŪūŬŭŮůŰűŲųŴŵŶŷŸŹźŻżŽžſƀƁƂƃƄƅƆƇƈƉƊƋƌƍƎƏƐƑƒ
+ƓƔƕƖƗƘƙƚƛƜƝƞƟƠơƢƣƤƥƦƧƨƩƪƫƬƭƮƯưƱƲƳƴƵƶƷƸƹƺƼƽƾƿDŽdžLJljNJnjǍǎǏǐǑǒǓǔǕǖǗǘǙǚǛǜ
+ǝǞǟǠǡǢǣǤǥǦǧǨǩǪǫǬǭǮǯǰDZdzǴǵǶǷǸǹǺǻǼǽǾǿȀȁȂȃȄȅȆȇȈȉȊȋȌȍȎȏȐȑȒȓȔȕȖȗȘșȚțȜȝȞȟ
+ȠȡȢȣȤȥȦȧȨȩȪȫȬȭȮȯȰȱȲȳȴȵȶȷȸȹȺȻȼȽȾȿɀɁɐɑɒɓɔɕɖɗɘəɚɛɜɝɞɟɠɡɢɣɤɥɦɧɨɩɪɫɬɭɮɯ
+ɰɱɲɳɴɵɶɷɸɹɺɻɼɽɾɿʀʁʂʃʄʅʆʇʈʉʊʋʌʍʎʏʐʑʒʓʔʕʖʗʘʙʚʛʜʝʞʟʠʡʢʣʤʥʦʧʨʩʪʫʬʭʮʯΆΈ
+ΉΊΌΎΏΐΑΒΓΔΕΖΗΘΙΚΛΜΝΞΟΠΡΣΤΥΦΧΨΩΪΫάέήίΰαβγδεζηθικλμνξοπρςστυφχψωϊϋόύ
+ώϐϑϒϓϔϕϖϗϘϙϚϛϜϝϞϟϠϡϢϣϤϥϦϧϨϩϪϫϬϭϮϯϰϱϲϳϴϵϷϸϹϺϻϼϽϾϿЀЁЂЃЄЅІЇЈЉЊЋЌЍЎЏАБ
+ВГДЕЖЗИЙКЛМНОПРСТУФХЦЧШЩЪЫЬЭЮЯабвгдежзийклмнопрстуфхцчшщъыьэюяѐёђѓ
+єѕіїјљњћќѝўџѠѡѢѣѤѥѦѧѨѩѪѫѬѭѮѯѰѱѲѳѴѵѶѷѸѹѺѻѼѽѾѿҀҁҊҋҌҍҎҏҐґҒғҔҕҖҗҘҙҚқҜҝ
+ҞҟҠҡҢңҤҥҦҧҨҩҪҫҬҭҮүҰұҲҳҴҵҶҷҸҹҺһҼҽҾҿӀӁӂӃӄӅӆӇӈӉӊӋӌӍӎӐӑӒӓӔӕӖӗӘәӚӛӜӝӞӟӠ
+ӡӢӣӤӥӦӧӨөӪӫӬӭӮӯӰӱӲӳӴӵӶӷӸӹԀԁԂԃԄԅԆԇԈԉԊԋԌԍԎԏԱԲԳԴԵԶԷԸԹԺԻԼԽԾԿՀՁՂՃՄՅՆՇՈՉ
+ՊՋՌՍՎՏՐՑՒՓՔՕՖաբգդեզէըթժիլխծկհձղճմյնշոչպջռսվտրցւփքօֆևႠႡႢႣႤႥႦႧႨႩႪႫႬႭ
+ႮႯႰႱႲႳႴႵႶႷႸႹႺႻႼႽႾႿჀჁჂჃჄჅᴀᴁᴂᴃᴄᴅᴆᴇᴈᴉᴊᴋᴌᴍᴎᴏᴐᴑᴒᴓᴔᴕᴖᴗᴘᴙᴚᴛᴜᴝᴞᴟᴠᴡᴢᴣᴤᴥᴦᴧᴨᴩ
+ᴪᴫᵢᵣᵤᵥᵦᵧᵨᵩᵪᵫᵬᵭᵮᵯᵰᵱᵲᵳᵴᵵᵶᵷᵹᵺᵻᵼᵽᵾᵿᶀᶁᶂᶃᶄᶅᶆᶇᶈᶉᶊᶋᶌᶍᶎᶏᶐᶑᶒᶓᶔᶕᶖᶗᶘᶙᶚḀḁḂḃḄḅḆḇ
+ḈḉḊḋḌḍḎḏḐḑḒḓḔḕḖḗḘḙḚḛḜḝḞḟḠḡḢḣḤḥḦḧḨḩḪḫḬḭḮḯḰḱḲḳḴḵḶḷḸḹḺḻḼḽḾḿṀṁṂṃṄṅṆṇṈṉ
+ṊṋṌṍṎṏṐṑṒṓṔṕṖṗṘṙṚṛṜṝṞṟṠṡṢṣṤṥṦṧṨṩṪṫṬṭṮṯṰṱṲṳṴṵṶṷṸṹṺṻṼṽṾṿẀẁẂẃẄẅẆẇẈẉẊẋ
+ẌẍẎẏẐẑẒẓẔẕẖẗẘẙẚẛẠạẢảẤấẦầẨẩẪẫẬậẮắẰằẲẳẴẵẶặẸẹẺẻẼẽẾếỀềỂểỄễỆệỈỉỊịỌọỎỏỐố
+ỒồỔổỖỗỘộỚớỜờỞởỠỡỢợỤụỦủỨứỪừỬửỮữỰựỲỳỴỵỶỷỸỹἀἁἂἃἄἅἆἇἈἉἊἋἌἍἎἏἐἑἒἓἔἕἘἙἚἛ
+ἜἝἠἡἢἣἤἥἦἧἨἩἪἫἬἭἮἯἰἱἲἳἴἵἶἷἸἹἺἻἼἽἾἿὀὁὂὃὄὅὈὉὊὋὌὍὐὑὒὓὔὕὖὗὙὛὝὟὠὡὢὣὤὥὦὧ
+ὨὩὪὫὬὭὮὯὰάὲέὴήὶίὸόὺύὼώᾀᾁᾂᾃᾄᾅᾆᾇᾐᾑᾒᾓᾔᾕᾖᾗᾠᾡᾢᾣᾤᾥᾦᾧᾰᾱᾲᾳᾴᾶᾷᾸᾹᾺΆιῂῃῄῆῇῈΈῊ
+ΉῐῑῒΐῖῗῘῙῚΊῠῡῢΰῤῥῦῧῨῩῪΎῬῲῳῴῶῷῸΌῺΏⁱⁿℂℇℊℋℌℍℎℏℐℑℒℓℕℙℚℛℜℝℤΩℨKÅℬℭℯℰℱℳℴℹ
+ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyzªµºÀÁÂÃÄÅÆÇÈÉÊ
+ËÌÍÎÏÐÑÒÓÔÕÖØÙÚÛÜÝÞßàáâãäåæçèéêëìíîïðñòóôõöøùúûüýþÿĀāĂ㥹ĆćĈĉĊċČčĎ
+ďĐđĒēĔĕĖėĘęĚěĜĝĞğĠġĢģĤĥĦħĨĩĪīĬĭĮįİıIJijĴĵĶķĸĹĺĻļĽľĿŀŁłŃńŅņŇňʼnŊŋŌōŎŏŐ
+őŒœŔŕŖŗŘřŚśŜŝŞşŠšŢţŤťŦŧŨũŪūŬŭŮůŰűŲųŴŵŶŷŸŹźŻżŽžſƀƁƂƃƄƅƆƇƈƉƊƋƌƍƎƏƐƑƒ
+ƓƔƕƖƗƘƙƚƛƜƝƞƟƠơƢƣƤƥƦƧƨƩƪƫƬƭƮƯưƱƲƳƴƵƶƷƸƹƺƼƽƾƿDŽdžLJljNJnjǍǎǏǐǑǒǓǔǕǖǗǘǙǚǛǜ
+ǝǞǟǠǡǢǣǤǥǦǧǨǩǪǫǬǭǮǯǰDZdzǴǵǶǷǸǹǺǻǼǽǾǿȀȁȂȃȄȅȆȇȈȉȊȋȌȍȎȏȐȑȒȓȔȕȖȗȘșȚțȜȝȞȟ
+ȠȡȢȣȤȥȦȧȨȩȪȫȬȭȮȯȰȱȲȳȴȵȶȷȸȹȺȻȼȽȾȿɀɁɐɑɒɓɔɕɖɗɘəɚɛɜɝɞɟɠɡɢɣɤɥɦɧɨɩɪɫɬɭɮɯ
+ɰɱɲɳɴɵɶɷɸɹɺɻɼɽɾɿʀʁʂʃʄʅʆʇʈʉʊʋʌʍʎʏʐʑʒʓʔʕʖʗʘʙʚʛʜʝʞʟʠʡʢʣʤʥʦʧʨʩʪʫʬʭʮʯΆΈ
+ΉΊΌΎΏΐΑΒΓΔΕΖΗΘΙΚΛΜΝΞΟΠΡΣΤΥΦΧΨΩΪΫάέήίΰαβγδεζηθικλμνξοπρςστυφχψωϊϋόύ
+ώϐϑϒϓϔϕϖϗϘϙϚϛϜϝϞϟϠϡϢϣϤϥϦϧϨϩϪϫϬϭϮϯϰϱϲϳϴϵϷϸϹϺϻϼϽϾϿЀЁЂЃЄЅІЇЈЉЊЋЌЍЎЏАБ
+ВГДЕЖЗИЙКЛМНОПРСТУФХЦЧШЩЪЫЬЭЮЯабвгдежзийклмнопрстуфхцчшщъыьэюяѐёђѓ
+єѕіїјљњћќѝўџѠѡѢѣѤѥѦѧѨѩѪѫѬѭѮѯѰѱѲѳѴѵѶѷѸѹѺѻѼѽѾѿҀҁҊҋҌҍҎҏҐґҒғҔҕҖҗҘҙҚқҜҝ
+ҞҟҠҡҢңҤҥҦҧҨҩҪҫҬҭҮүҰұҲҳҴҵҶҷҸҹҺһҼҽҾҿӀӁӂӃӄӅӆӇӈӉӊӋӌӍӎӐӑӒӓӔӕӖӗӘәӚӛӜӝӞӟӠ
+ӡӢӣӤӥӦӧӨөӪӫӬӭӮӯӰӱӲӳӴӵӶӷӸӹԀԁԂԃԄԅԆԇԈԉԊԋԌԍԎԏԱԲԳԴԵԶԷԸԹԺԻԼԽԾԿՀՁՂՃՄՅՆՇՈՉ
+ՊՋՌՍՎՏՐՑՒՓՔՕՖաբգդեզէըթժիլխծկհձղճմյնշոչպջռսվտրցւփքօֆևႠႡႢႣႤႥႦႧႨႩႪႫႬႭ
+ႮႯႰႱႲႳႴႵႶႷႸႹႺႻႼႽႾႿჀჁჂჃჄჅᴀᴁᴂᴃᴄᴅᴆᴇᴈᴉᴊᴋᴌᴍᴎᴏᴐᴑᴒᴓᴔᴕᴖᴗᴘᴙᴚᴛᴜᴝᴞᴟᴠᴡᴢᴣᴤᴥᴦᴧᴨᴩ
+ᴪᴫᵢᵣᵤᵥᵦᵧᵨᵩᵪᵫᵬᵭᵮᵯᵰᵱᵲᵳᵴᵵᵶᵷᵹᵺᵻᵼᵽᵾᵿᶀᶁᶂᶃᶄᶅᶆᶇᶈᶉᶊᶋᶌᶍᶎᶏᶐᶑᶒᶓᶔᶕᶖᶗᶘᶙᶚḀḁḂḃḄḅḆḇ
+ḈḉḊḋḌḍḎḏḐḑḒḓḔḕḖḗḘḙḚḛḜḝḞḟḠḡḢḣḤḥḦḧḨḩḪḫḬḭḮḯḰḱḲḳḴḵḶḷḸḹḺḻḼḽḾḿṀṁṂṃṄṅṆṇṈṉ
+ṊṋṌṍṎṏṐṑṒṓṔṕṖṗṘṙṚṛṜṝṞṟṠṡṢṣṤṥṦṧṨṩṪṫṬṭṮṯṰṱṲṳṴṵṶṷṸṹṺṻṼṽṾṿẀẁẂẃẄẅẆẇẈẉẊẋ
+ẌẍẎẏẐẑẒẓẔẕẖẗẘẙẚẛẠạẢảẤấẦầẨẩẪẫẬậẮắẰằẲẳẴẵẶặẸẹẺẻẼẽẾếỀềỂểỄễỆệỈỉỊịỌọỎỏỐố
+ỒồỔổỖỗỘộỚớỜờỞởỠỡỢợỤụỦủỨứỪừỬửỮữỰựỲỳỴỵỶỷỸỹἀἁἂἃἄἅἆἇἈἉἊἋἌἍἎἏἐἑἒἓἔἕἘἙἚἛ
+ἜἝἠἡἢἣἤἥἦἧἨἩἪἫἬἭἮἯἰἱἲἳἴἵἶἷἸἹἺἻἼἽἾἿὀὁὂὃὄὅὈὉὊὋὌὍὐὑὒὓὔὕὖὗὙὛὝὟὠὡὢὣὤὥὦὧ
+ὨὩὪὫὬὭὮὯὰάὲέὴήὶίὸόὺύὼώᾀᾁᾂᾃᾄᾅᾆᾇᾐᾑᾒᾓᾔᾕᾖᾗᾠᾡᾢᾣᾤᾥᾦᾧᾰᾱᾲᾳᾴᾶᾷᾸᾹᾺΆιῂῃῄῆῇῈΈῊ
+ΉῐῑῒΐῖῗῘῙῚΊῠῡῢΰῤῥῦῧῨῩῪΎῬῲῳῴῶῷῸΌῺΏⁱⁿℂℇℊℋℌℍℎℏℐℑℒℓℕℙℚℛℜℝℤΩℨKÅℬℭℯℰℱℳℴℹ
+ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyzªµºÀÁÂÃÄÅÆÇÈÉÊ
+ËÌÍÎÏÐÑÒÓÔÕÖØÙÚÛÜÝÞßàáâãäåæçèéêëìíîïðñòóôõöøùúûüýþÿĀāĂ㥹ĆćĈĉĊċČčĎ
+ďĐđĒēĔĕĖėĘęĚěĜĝĞğĠġĢģĤĥĦħĨĩĪīĬĭĮįİıIJijĴĵĶķĸĹĺĻļĽľĿŀŁłŃńŅņŇňʼnŊŋŌōŎŏŐ
+őŒœŔŕŖŗŘřŚśŜŝŞşŠšŢţŤťŦŧŨũŪūŬŭŮůŰűŲųŴŵŶŷŸŹźŻżŽžſƀƁƂƃƄƅƆƇƈƉƊƋƌƍƎƏƐƑƒ
+ƓƔƕƖƗƘƙƚƛƜƝƞƟƠơƢƣƤƥƦƧƨƩƪƫƬƭƮƯưƱƲƳƴƵƶƷƸƹƺƼƽƾƿDŽdžLJljNJnjǍǎǏǐǑǒǓǔǕǖǗǘǙǚǛǜ
+ǝǞǟǠǡǢǣǤǥǦǧǨǩǪǫǬǭǮǯǰDZdzǴǵǶǷǸǹǺǻǼǽǾǿȀȁȂȃȄȅȆȇȈȉȊȋȌȍȎȏȐȑȒȓȔȕȖȗȘșȚțȜȝȞȟ
+ȠȡȢȣȤȥȦȧȨȩȪȫȬȭȮȯȰȱȲȳȴȵȶȷȸȹȺȻȼȽȾȿɀɁɐɑɒɓɔɕɖɗɘəɚɛɜɝɞɟɠɡɢɣɤɥɦɧɨɩɪɫɬɭɮɯ
+ɰɱɲɳɴɵɶɷɸɹɺɻɼɽɾɿʀʁʂʃʄʅʆʇʈʉʊʋʌʍʎʏʐʑʒʓʔʕʖʗʘʙʚʛʜʝʞʟʠʡʢʣʤʥʦʧʨʩʪʫʬʭʮʯΆΈ
+ΉΊΌΎΏΐΑΒΓΔΕΖΗΘΙΚΛΜΝΞΟΠΡΣΤΥΦΧΨΩΪΫάέήίΰαβγδεζηθικλμνξοπρςστυφχψωϊϋόύ
+ώϐϑϒϓϔϕϖϗϘϙϚϛϜϝϞϟϠϡϢϣϤϥϦϧϨϩϪϫϬϭϮϯϰϱϲϳϴϵϷϸϹϺϻϼϽϾϿЀЁЂЃЄЅІЇЈЉЊЋЌЍЎЏАБ
+ВГДЕЖЗИЙКЛМНОПРСТУФХЦЧШЩЪЫЬЭЮЯабвгдежзийклмнопрстуфхцчшщъыьэюяѐёђѓ
+єѕіїјљњћќѝўџѠѡѢѣѤѥѦѧѨѩѪѫѬѭѮѯѰѱѲѳѴѵѶѷѸѹѺѻѼѽѾѿҀҁҊҋҌҍҎҏҐґҒғҔҕҖҗҘҙҚқҜҝ
+ҞҟҠҡҢңҤҥҦҧҨҩҪҫҬҭҮүҰұҲҳҴҵҶҷҸҹҺһҼҽҾҿӀӁӂӃӄӅӆӇӈӉӊӋӌӍӎӐӑӒӓӔӕӖӗӘәӚӛӜӝӞӟӠ
+ӡӢӣӤӥӦӧӨөӪӫӬӭӮӯӰӱӲӳӴӵӶӷӸӹԀԁԂԃԄԅԆԇԈԉԊԋԌԍԎԏԱԲԳԴԵԶԷԸԹԺԻԼԽԾԿՀՁՂՃՄՅՆՇՈՉ
+ՊՋՌՍՎՏՐՑՒՓՔՕՖաբգդեզէըթժիլխծկհձղճմյնշոչպջռսվտրցւփքօֆևႠႡႢႣႤႥႦႧႨႩႪႫႬႭ
+ႮႯႰႱႲႳႴႵႶႷႸႹႺႻႼႽႾႿჀჁჂჃჄჅᴀᴁᴂᴃᴄᴅᴆᴇᴈᴉᴊᴋᴌᴍᴎᴏᴐᴑᴒᴓᴔᴕᴖᴗᴘᴙᴚᴛᴜᴝᴞᴟᴠᴡᴢᴣᴤᴥᴦᴧᴨᴩ
+ᴪᴫᵢᵣᵤᵥᵦᵧᵨᵩᵪᵫᵬᵭᵮᵯᵰᵱᵲᵳᵴᵵᵶᵷᵹᵺᵻᵼᵽᵾᵿᶀᶁᶂᶃᶄᶅᶆᶇᶈᶉᶊᶋᶌᶍᶎᶏᶐᶑᶒᶓᶔᶕᶖᶗᶘᶙᶚḀḁḂḃḄḅḆḇ
+ḈḉḊḋḌḍḎḏḐḑḒḓḔḕḖḗḘḙḚḛḜḝḞḟḠḡḢḣḤḥḦḧḨḩḪḫḬḭḮḯḰḱḲḳḴḵḶḷḸḹḺḻḼḽḾḿṀṁṂṃṄṅṆṇṈṉ
+ṊṋṌṍṎṏṐṑṒṓṔṕṖṗṘṙṚṛṜṝṞṟṠṡṢṣṤṥṦṧṨṩṪṫṬṭṮṯṰṱṲṳṴṵṶṷṸṹṺṻṼṽṾṿẀẁẂẃẄẅẆẇẈẉẊẋ
+ẌẍẎẏẐẑẒẓẔẕẖẗẘẙẚẛẠạẢảẤấẦầẨẩẪẫẬậẮắẰằẲẳẴẵẶặẸẹẺẻẼẽẾếỀềỂểỄễỆệỈỉỊịỌọỎỏỐố
+ỒồỔổỖỗỘộỚớỜờỞởỠỡỢợỤụỦủỨứỪừỬửỮữỰựỲỳỴỵỶỷỸỹἀἁἂἃἄἅἆἇἈἉἊἋἌἍἎἏἐἑἒἓἔἕἘἙἚἛ
+ἜἝἠἡἢἣἤἥἦἧἨἩἪἫἬἭἮἯἰἱἲἳἴἵἶἷἸἹἺἻἼἽἾἿὀὁὂὃὄὅὈὉὊὋὌὍὐὑὒὓὔὕὖὗὙὛὝὟὠὡὢὣὤὥὦὧ
+ὨὩὪὫὬὭὮὯὰάὲέὴήὶίὸόὺύὼώᾀᾁᾂᾃᾄᾅᾆᾇᾐᾑᾒᾓᾔᾕᾖᾗᾠᾡᾢᾣᾤᾥᾦᾧᾰᾱᾲᾳᾴᾶᾷᾸᾹᾺΆιῂῃῄῆῇῈΈῊ
+ΉῐῑῒΐῖῗῘῙῚΊῠῡῢΰῤῥῦῧῨῩῪΎῬῲῳῴῶῷῸΌῺΏⁱⁿℂℇℊℋℌℍℎℏℐℑℒℓℕℙℚℛℜℝℤΩℨKÅℬℭℯℰℱℳℴℹ
diff --git a/_test/data/mappings.events b/_test/data/mappings.events
new file mode 100644
index 0000000..3cb5579
--- /dev/null
+++ b/_test/data/mappings.events
@@ -0,0 +1,44 @@
+- !StreamStart
+
+- !DocumentStart
+- !MappingStart
+- !Scalar { implicit: [true,true], value: 'key' }
+- !Scalar { implicit: [true,true], value: 'value' }
+- !Scalar { implicit: [true,true], value: 'empty mapping' }
+- !MappingStart
+- !MappingEnd
+- !Scalar { implicit: [true,true], value: 'empty mapping with tag' }
+- !MappingStart { tag: '!mytag', implicit: false }
+- !MappingEnd
+- !Scalar { implicit: [true,true], value: 'block mapping' }
+- !MappingStart
+- !MappingStart
+- !Scalar { implicit: [true,true], value: 'complex' }
+- !Scalar { implicit: [true,true], value: 'key' }
+- !Scalar { implicit: [true,true], value: 'complex' }
+- !Scalar { implicit: [true,true], value: 'key' }
+- !MappingEnd
+- !MappingStart
+- !Scalar { implicit: [true,true], value: 'complex' }
+- !Scalar { implicit: [true,true], value: 'key' }
+- !MappingEnd
+- !MappingEnd
+- !Scalar { implicit: [true,true], value: 'flow mapping' }
+- !MappingStart { flow_style: true }
+- !Scalar { implicit: [true,true], value: 'key' }
+- !Scalar { implicit: [true,true], value: 'value' }
+- !MappingStart
+- !Scalar { implicit: [true,true], value: 'complex' }
+- !Scalar { implicit: [true,true], value: 'key' }
+- !Scalar { implicit: [true,true], value: 'complex' }
+- !Scalar { implicit: [true,true], value: 'key' }
+- !MappingEnd
+- !MappingStart
+- !Scalar { implicit: [true,true], value: 'complex' }
+- !Scalar { implicit: [true,true], value: 'key' }
+- !MappingEnd
+- !MappingEnd
+- !MappingEnd
+- !DocumentEnd
+
+- !StreamEnd
diff --git a/_test/data/merge.data b/_test/data/merge.data
new file mode 100644
index 0000000..e455bbc
--- /dev/null
+++ b/_test/data/merge.data
@@ -0,0 +1 @@
+- <<
diff --git a/_test/data/merge.detect b/_test/data/merge.detect
new file mode 100644
index 0000000..1672d0d
--- /dev/null
+++ b/_test/data/merge.detect
@@ -0,0 +1 @@
+tag:yaml.org,2002:merge
diff --git a/_test/data/more-floats.code b/_test/data/more-floats.code
new file mode 100644
index 0000000..e3e444e
--- /dev/null
+++ b/_test/data/more-floats.code
@@ -0,0 +1 @@
+[0.0, +1.0, -1.0, +1e300000, -1e300000, 1e300000/1e300000, -(1e300000/1e300000)] # last two items are ind and qnan respectively.
diff --git a/_test/data/more-floats.data b/_test/data/more-floats.data
new file mode 100644
index 0000000..399eb17
--- /dev/null
+++ b/_test/data/more-floats.data
@@ -0,0 +1 @@
+[0.0, +1.0, -1.0, +.inf, -.inf, .nan, .nan]
diff --git a/_test/data/negative-float-bug.code b/_test/data/negative-float-bug.code
new file mode 100644
index 0000000..18e16e3
--- /dev/null
+++ b/_test/data/negative-float-bug.code
@@ -0,0 +1 @@
+-1.0
diff --git a/_test/data/negative-float-bug.data b/_test/data/negative-float-bug.data
new file mode 100644
index 0000000..18e16e3
--- /dev/null
+++ b/_test/data/negative-float-bug.data
@@ -0,0 +1 @@
+-1.0
diff --git a/_test/data/no-alias-anchor.emitter-error b/_test/data/no-alias-anchor.emitter-error
new file mode 100644
index 0000000..5ff065c
--- /dev/null
+++ b/_test/data/no-alias-anchor.emitter-error
@@ -0,0 +1,8 @@
+- !StreamStart
+- !DocumentStart
+- !SequenceStart
+- !Scalar { anchor: A, value: data }
+- !Alias { }
+- !SequenceEnd
+- !DocumentEnd
+- !StreamEnd
diff --git a/_test/data/no-alias-anchor.skip-ext b/_test/data/no-alias-anchor.skip-ext
new file mode 100644
index 0000000..e69de29
--- /dev/null
+++ b/_test/data/no-alias-anchor.skip-ext
diff --git a/_test/data/no-block-collection-end.loader-error b/_test/data/no-block-collection-end.loader-error
new file mode 100644
index 0000000..02d4d37
--- /dev/null
+++ b/_test/data/no-block-collection-end.loader-error
@@ -0,0 +1,3 @@
+- foo
+- bar
+baz: bar
diff --git a/_test/data/no-block-mapping-end-2.loader-error b/_test/data/no-block-mapping-end-2.loader-error
new file mode 100644
index 0000000..be63571
--- /dev/null
+++ b/_test/data/no-block-mapping-end-2.loader-error
@@ -0,0 +1,3 @@
+? foo
+: bar
+: baz
diff --git a/_test/data/no-block-mapping-end.loader-error b/_test/data/no-block-mapping-end.loader-error
new file mode 100644
index 0000000..1ea921c
--- /dev/null
+++ b/_test/data/no-block-mapping-end.loader-error
@@ -0,0 +1 @@
+foo: "bar" "baz"
diff --git a/_test/data/no-document-start.loader-error b/_test/data/no-document-start.loader-error
new file mode 100644
index 0000000..c725ec8
--- /dev/null
+++ b/_test/data/no-document-start.loader-error
@@ -0,0 +1,3 @@
+%YAML 1.1
+# no ---
+foo: bar
diff --git a/_test/data/no-flow-mapping-end.loader-error b/_test/data/no-flow-mapping-end.loader-error
new file mode 100644
index 0000000..8bd1403
--- /dev/null
+++ b/_test/data/no-flow-mapping-end.loader-error
@@ -0,0 +1 @@
+{ foo: bar ]
diff --git a/_test/data/no-flow-sequence-end.loader-error b/_test/data/no-flow-sequence-end.loader-error
new file mode 100644
index 0000000..750d973
--- /dev/null
+++ b/_test/data/no-flow-sequence-end.loader-error
@@ -0,0 +1 @@
+[foo, bar}
diff --git a/_test/data/no-node-1.loader-error b/_test/data/no-node-1.loader-error
new file mode 100644
index 0000000..07b1500
--- /dev/null
+++ b/_test/data/no-node-1.loader-error
@@ -0,0 +1 @@
+- !foo ]
diff --git a/_test/data/no-node-2.loader-error b/_test/data/no-node-2.loader-error
new file mode 100644
index 0000000..563e3b3
--- /dev/null
+++ b/_test/data/no-node-2.loader-error
@@ -0,0 +1 @@
+- [ !foo } ]
diff --git a/_test/data/no-tag.emitter-error b/_test/data/no-tag.emitter-error
new file mode 100644
index 0000000..384c62f
--- /dev/null
+++ b/_test/data/no-tag.emitter-error
@@ -0,0 +1,5 @@
+- !StreamStart
+- !DocumentStart
+- !Scalar { value: 'foo', implicit: [false,false] }
+- !DocumentEnd
+- !StreamEnd
diff --git a/_test/data/null.data b/_test/data/null.data
new file mode 100644
index 0000000..ad12528
--- /dev/null
+++ b/_test/data/null.data
@@ -0,0 +1,3 @@
+-
+- ~
+- null
diff --git a/_test/data/null.detect b/_test/data/null.detect
new file mode 100644
index 0000000..19110c7
--- /dev/null
+++ b/_test/data/null.detect
@@ -0,0 +1 @@
+tag:yaml.org,2002:null
diff --git a/_test/data/odd-utf16.stream-error b/_test/data/odd-utf16.stream-error
new file mode 100644
index 0000000..b59e434
--- /dev/null
+++ b/_test/data/odd-utf16.stream-error
Binary files differ
diff --git a/_test/data/omap.data b/_test/data/omap.data
new file mode 100644
index 0000000..b366fbc
--- /dev/null
+++ b/_test/data/omap.data
@@ -0,0 +1,8 @@
+Bestiary: !!omap
+- aardvark: African pig-like ant eater. Ugly.
+- anteater: South-American ant eater. Two species.
+- anaconda: South-American constrictor snake. Scaly.
+Numbers: !!omap
+- one: 1
+- two: 2
+- three: 3
diff --git a/_test/data/omap.roundtrip b/_test/data/omap.roundtrip
new file mode 100644
index 0000000..e69de29
--- /dev/null
+++ b/_test/data/omap.roundtrip
diff --git a/_test/data/recursive-anchor.former-loader-error b/_test/data/recursive-anchor.former-loader-error
new file mode 100644
index 0000000..661166c
--- /dev/null
+++ b/_test/data/recursive-anchor.former-loader-error
@@ -0,0 +1,4 @@
+- &foo [1
+ 2,
+ 3,
+ *foo]
diff --git a/_test/data/recursive-dict.recursive b/_test/data/recursive-dict.recursive
new file mode 100644
index 0000000..8f326f5
--- /dev/null
+++ b/_test/data/recursive-dict.recursive
@@ -0,0 +1,3 @@
+value = {}
+instance = AnInstance(value, value)
+value[instance] = instance
diff --git a/_test/data/recursive-list.recursive b/_test/data/recursive-list.recursive
new file mode 100644
index 0000000..27a4ae5
--- /dev/null
+++ b/_test/data/recursive-list.recursive
@@ -0,0 +1,2 @@
+value = []
+value.append(value)
diff --git a/_test/data/recursive-set.recursive b/_test/data/recursive-set.recursive
new file mode 100644
index 0000000..457c50d
--- /dev/null
+++ b/_test/data/recursive-set.recursive
@@ -0,0 +1,7 @@
+try:
+ set
+except NameError:
+ from sets import Set as set
+value = set()
+value.add(AnInstance(foo=value, bar=value))
+value.add(AnInstance(foo=value, bar=value))
diff --git a/_test/data/recursive-state.recursive b/_test/data/recursive-state.recursive
new file mode 100644
index 0000000..bffe61e
--- /dev/null
+++ b/_test/data/recursive-state.recursive
@@ -0,0 +1,2 @@
+value = []
+value.append(AnInstanceWithState(value, value))
diff --git a/_test/data/recursive-tuple.recursive b/_test/data/recursive-tuple.recursive
new file mode 100644
index 0000000..dc08d02
--- /dev/null
+++ b/_test/data/recursive-tuple.recursive
@@ -0,0 +1,3 @@
+value = ([], [])
+value[0].append(value)
+value[1].append(value[0])
diff --git a/_test/data/recursive.former-dumper-error b/_test/data/recursive.former-dumper-error
new file mode 100644
index 0000000..3c7cc2f
--- /dev/null
+++ b/_test/data/recursive.former-dumper-error
@@ -0,0 +1,3 @@
+data = []
+data.append(data)
+dump(data)
diff --git a/_test/data/remove-possible-simple-key-bug.loader-error b/_test/data/remove-possible-simple-key-bug.loader-error
new file mode 100644
index 0000000..fe1bc6c
--- /dev/null
+++ b/_test/data/remove-possible-simple-key-bug.loader-error
@@ -0,0 +1,3 @@
+foo: &A bar
+*A ] # The ']' indicator triggers remove_possible_simple_key,
+ # which should raise an error.
diff --git a/_test/data/resolver.data b/_test/data/resolver.data
new file mode 100644
index 0000000..a296404
--- /dev/null
+++ b/_test/data/resolver.data
@@ -0,0 +1,30 @@
+---
+"this scalar should be selected"
+---
+key11: !foo
+ key12:
+ is: [selected]
+ key22:
+ key13: [not, selected]
+ key23: [not, selected]
+ key32:
+ key31: [not, selected]
+ key32: [not, selected]
+ key33: {not: selected}
+key21: !bar
+ - not selected
+ - selected
+ - not selected
+key31: !baz
+ key12:
+ key13:
+ key14: {selected}
+ key23:
+ key14: [not, selected]
+ key33:
+ key14: {selected}
+ key24: {not: selected}
+ key22:
+ - key14: {selected}
+ key24: {not: selected}
+ - key14: {selected}
diff --git a/_test/data/resolver.path b/_test/data/resolver.path
new file mode 100644
index 0000000..ec677d2
--- /dev/null
+++ b/_test/data/resolver.path
@@ -0,0 +1,30 @@
+--- !root/scalar
+"this scalar should be selected"
+--- !root
+key11: !foo
+ key12: !root/key11/key12/*
+ is: [selected]
+ key22:
+ key13: [not, selected]
+ key23: [not, selected]
+ key32:
+ key31: [not, selected]
+ key32: [not, selected]
+ key33: {not: selected}
+key21: !bar
+ - not selected
+ - !root/key21/1/* selected
+ - not selected
+key31: !baz
+ key12:
+ key13:
+ key14: !root/key31/*/*/key14/map {selected}
+ key23:
+ key14: [not, selected]
+ key33:
+ key14: !root/key31/*/*/key14/map {selected}
+ key24: {not: selected}
+ key22:
+ - key14: !root/key31/*/*/key14/map {selected}
+ key24: {not: selected}
+ - key14: !root/key31/*/*/key14/map {selected}
diff --git a/_test/data/run-parser-crash-bug.data b/_test/data/run-parser-crash-bug.data
new file mode 100644
index 0000000..fe01734
--- /dev/null
+++ b/_test/data/run-parser-crash-bug.data
@@ -0,0 +1,8 @@
+---
+- Harry Potter and the Prisoner of Azkaban
+- Harry Potter and the Goblet of Fire
+- Harry Potter and the Order of the Phoenix
+---
+- Memoirs Found in a Bathtub
+- Snow Crash
+- Ghost World
diff --git a/_test/data/scalars.events b/_test/data/scalars.events
new file mode 100644
index 0000000..32c40f4
--- /dev/null
+++ b/_test/data/scalars.events
@@ -0,0 +1,28 @@
+- !StreamStart
+
+- !DocumentStart
+- !MappingStart
+- !Scalar { implicit: [true,true], value: 'empty scalar' }
+- !Scalar { implicit: [true,false], value: '' }
+- !Scalar { implicit: [true,true], value: 'implicit scalar' }
+- !Scalar { implicit: [true,true], value: 'data' }
+- !Scalar { implicit: [true,true], value: 'quoted scalar' }
+- !Scalar { value: 'data', style: '"' }
+- !Scalar { implicit: [true,true], value: 'block scalar' }
+- !Scalar { value: 'data', style: '|' }
+- !Scalar { implicit: [true,true], value: 'empty scalar with tag' }
+- !Scalar { implicit: [false,false], tag: '!mytag', value: '' }
+- !Scalar { implicit: [true,true], value: 'implicit scalar with tag' }
+- !Scalar { implicit: [false,false], tag: '!mytag', value: 'data' }
+- !Scalar { implicit: [true,true], value: 'quoted scalar with tag' }
+- !Scalar { value: 'data', style: '"', tag: '!mytag', implicit: [false,false] }
+- !Scalar { implicit: [true,true], value: 'block scalar with tag' }
+- !Scalar { value: 'data', style: '|', tag: '!mytag', implicit: [false,false] }
+- !Scalar { implicit: [true,true], value: 'single character' }
+- !Scalar { value: 'a', implicit: [true,true] }
+- !Scalar { implicit: [true,true], value: 'single digit' }
+- !Scalar { value: '1', implicit: [true,false] }
+- !MappingEnd
+- !DocumentEnd
+
+- !StreamEnd
diff --git a/_test/data/scan-document-end-bug.canonical b/_test/data/scan-document-end-bug.canonical
new file mode 100644
index 0000000..4a0e8a8
--- /dev/null
+++ b/_test/data/scan-document-end-bug.canonical
@@ -0,0 +1,3 @@
+%YAML 1.1
+---
+!!null ""
diff --git a/_test/data/scan-document-end-bug.data b/_test/data/scan-document-end-bug.data
new file mode 100644
index 0000000..3c70543
--- /dev/null
+++ b/_test/data/scan-document-end-bug.data
@@ -0,0 +1,3 @@
+# Ticket #4
+---
+... \ No newline at end of file
diff --git a/_test/data/scan-line-break-bug.canonical b/_test/data/scan-line-break-bug.canonical
new file mode 100644
index 0000000..79f08b7
--- /dev/null
+++ b/_test/data/scan-line-break-bug.canonical
@@ -0,0 +1,3 @@
+%YAML 1.1
+---
+!!map { ? !!str "foo" : !!str "bar baz" }
diff --git a/_test/data/scan-line-break-bug.data b/_test/data/scan-line-break-bug.data
new file mode 100644
index 0000000..c974fab
--- /dev/null
+++ b/_test/data/scan-line-break-bug.data
@@ -0,0 +1,3 @@
+foo:
+ bar
+ baz
diff --git a/_test/data/sequences.events b/_test/data/sequences.events
new file mode 100644
index 0000000..692a329
--- /dev/null
+++ b/_test/data/sequences.events
@@ -0,0 +1,81 @@
+- !StreamStart
+
+- !DocumentStart
+- !SequenceStart
+- !SequenceEnd
+- !DocumentEnd
+
+- !DocumentStart
+- !SequenceStart { tag: '!mytag', implicit: false }
+- !SequenceEnd
+- !DocumentEnd
+
+- !DocumentStart
+- !SequenceStart
+- !SequenceStart
+- !SequenceEnd
+- !SequenceStart { tag: '!mytag', implicit: false }
+- !SequenceEnd
+- !SequenceStart
+- !Scalar
+- !Scalar { value: 'data' }
+- !Scalar { tag: '!mytag', implicit: [false,false], value: 'data' }
+- !SequenceEnd
+- !SequenceStart
+- !SequenceStart
+- !SequenceStart
+- !Scalar
+- !SequenceEnd
+- !SequenceEnd
+- !SequenceEnd
+- !SequenceStart
+- !SequenceStart { tag: '!mytag', implicit: false }
+- !SequenceStart
+- !Scalar { value: 'data' }
+- !SequenceEnd
+- !SequenceEnd
+- !SequenceEnd
+- !SequenceEnd
+- !DocumentEnd
+
+- !DocumentStart
+- !SequenceStart
+- !MappingStart
+- !Scalar { value: 'key1' }
+- !SequenceStart
+- !Scalar { value: 'data1' }
+- !Scalar { value: 'data2' }
+- !SequenceEnd
+- !Scalar { value: 'key2' }
+- !SequenceStart { tag: '!mytag1', implicit: false }
+- !Scalar { value: 'data3' }
+- !SequenceStart
+- !Scalar { value: 'data4' }
+- !Scalar { value: 'data5' }
+- !SequenceEnd
+- !SequenceStart { tag: '!mytag2', implicit: false }
+- !Scalar { value: 'data6' }
+- !Scalar { value: 'data7' }
+- !SequenceEnd
+- !SequenceEnd
+- !MappingEnd
+- !SequenceEnd
+- !DocumentEnd
+
+- !DocumentStart
+- !SequenceStart
+- !SequenceStart { flow_style: true }
+- !SequenceStart
+- !SequenceEnd
+- !Scalar
+- !Scalar { value: 'data' }
+- !Scalar { tag: '!mytag', implicit: [false,false], value: 'data' }
+- !SequenceStart { tag: '!mytag', implicit: false }
+- !Scalar { value: 'data' }
+- !Scalar { value: 'data' }
+- !SequenceEnd
+- !SequenceEnd
+- !SequenceEnd
+- !DocumentEnd
+
+- !StreamEnd
diff --git a/_test/data/serializer-is-already-opened.dumper-error b/_test/data/serializer-is-already-opened.dumper-error
new file mode 100644
index 0000000..9a23525
--- /dev/null
+++ b/_test/data/serializer-is-already-opened.dumper-error
@@ -0,0 +1,3 @@
+dumper = yaml.Dumper(StringIO())
+dumper.open()
+dumper.open()
diff --git a/_test/data/serializer-is-closed-1.dumper-error b/_test/data/serializer-is-closed-1.dumper-error
new file mode 100644
index 0000000..8e7e600
--- /dev/null
+++ b/_test/data/serializer-is-closed-1.dumper-error
@@ -0,0 +1,4 @@
+dumper = yaml.Dumper(StringIO())
+dumper.open()
+dumper.close()
+dumper.open()
diff --git a/_test/data/serializer-is-closed-2.dumper-error b/_test/data/serializer-is-closed-2.dumper-error
new file mode 100644
index 0000000..89aef7e
--- /dev/null
+++ b/_test/data/serializer-is-closed-2.dumper-error
@@ -0,0 +1,4 @@
+dumper = yaml.Dumper(StringIO())
+dumper.open()
+dumper.close()
+dumper.serialize(yaml.ScalarNode(tag='!foo', value='bar'))
diff --git a/_test/data/serializer-is-not-opened-1.dumper-error b/_test/data/serializer-is-not-opened-1.dumper-error
new file mode 100644
index 0000000..8f22e73
--- /dev/null
+++ b/_test/data/serializer-is-not-opened-1.dumper-error
@@ -0,0 +1,2 @@
+dumper = yaml.Dumper(StringIO())
+dumper.close()
diff --git a/_test/data/serializer-is-not-opened-2.dumper-error b/_test/data/serializer-is-not-opened-2.dumper-error
new file mode 100644
index 0000000..ebd9df1
--- /dev/null
+++ b/_test/data/serializer-is-not-opened-2.dumper-error
@@ -0,0 +1,2 @@
+dumper = yaml.Dumper(StringIO())
+dumper.serialize(yaml.ScalarNode(tag='!foo', value='bar'))
diff --git a/_test/data/single-dot-is-not-float-bug.code b/_test/data/single-dot-is-not-float-bug.code
new file mode 100644
index 0000000..dcd0c2f
--- /dev/null
+++ b/_test/data/single-dot-is-not-float-bug.code
@@ -0,0 +1 @@
+'.'
diff --git a/_test/data/single-dot-is-not-float-bug.data b/_test/data/single-dot-is-not-float-bug.data
new file mode 100644
index 0000000..9c558e3
--- /dev/null
+++ b/_test/data/single-dot-is-not-float-bug.data
@@ -0,0 +1 @@
+.
diff --git a/_test/data/sloppy-indentation.canonical b/_test/data/sloppy-indentation.canonical
new file mode 100644
index 0000000..438bc04
--- /dev/null
+++ b/_test/data/sloppy-indentation.canonical
@@ -0,0 +1,18 @@
+%YAML 1.1
+---
+!!map {
+ ? !!str "in the block context"
+ : !!map {
+ ? !!str "indentation should be kept"
+ : !!map {
+ ? !!str "but in the flow context"
+ : !!seq [ !!str "it may be violated" ]
+ }
+ }
+}
+--- !!str
+"the parser does not require scalars to be indented with at least one space"
+--- !!str
+"the parser does not require scalars to be indented with at least one space"
+--- !!map
+{ ? !!str "foo": { ? !!str "bar" : !!str "quoted scalars may not adhere indentation" } }
diff --git a/_test/data/sloppy-indentation.data b/_test/data/sloppy-indentation.data
new file mode 100644
index 0000000..2eb4f5a
--- /dev/null
+++ b/_test/data/sloppy-indentation.data
@@ -0,0 +1,17 @@
+---
+in the block context:
+ indentation should be kept: {
+ but in the flow context: [
+it may be violated]
+}
+---
+the parser does not require scalars
+to be indented with at least one space
+...
+---
+"the parser does not require scalars
+to be indented with at least one space"
+---
+foo:
+ bar: 'quoted scalars
+may not adhere indentation'
diff --git a/_test/data/spec-02-01.code b/_test/data/spec-02-01.code
new file mode 100644
index 0000000..0e927a3
--- /dev/null
+++ b/_test/data/spec-02-01.code
@@ -0,0 +1 @@
+['Mark McGwire', 'Sammy Sosa', 'Ken Griffey']
diff --git a/_test/data/spec-02-01.data b/_test/data/spec-02-01.data
new file mode 100644
index 0000000..d12e671
--- /dev/null
+++ b/_test/data/spec-02-01.data
@@ -0,0 +1,3 @@
+- Mark McGwire
+- Sammy Sosa
+- Ken Griffey
diff --git a/_test/data/spec-02-01.structure b/_test/data/spec-02-01.structure
new file mode 100644
index 0000000..f532f4a
--- /dev/null
+++ b/_test/data/spec-02-01.structure
@@ -0,0 +1 @@
+[True, True, True]
diff --git a/_test/data/spec-02-01.tokens b/_test/data/spec-02-01.tokens
new file mode 100644
index 0000000..ce44cac
--- /dev/null
+++ b/_test/data/spec-02-01.tokens
@@ -0,0 +1 @@
+[[ , _ , _ , _ ]}
diff --git a/_test/data/spec-02-02.data b/_test/data/spec-02-02.data
new file mode 100644
index 0000000..7b7ec94
--- /dev/null
+++ b/_test/data/spec-02-02.data
@@ -0,0 +1,3 @@
+hr: 65 # Home runs
+avg: 0.278 # Batting average
+rbi: 147 # Runs Batted In
diff --git a/_test/data/spec-02-02.structure b/_test/data/spec-02-02.structure
new file mode 100644
index 0000000..aba1ced
--- /dev/null
+++ b/_test/data/spec-02-02.structure
@@ -0,0 +1 @@
+[(True, True), (True, True), (True, True)]
diff --git a/_test/data/spec-02-02.tokens b/_test/data/spec-02-02.tokens
new file mode 100644
index 0000000..e4e381b
--- /dev/null
+++ b/_test/data/spec-02-02.tokens
@@ -0,0 +1,5 @@
+{{
+? _ : _
+? _ : _
+? _ : _
+]}
diff --git a/_test/data/spec-02-03.data b/_test/data/spec-02-03.data
new file mode 100644
index 0000000..656d628
--- /dev/null
+++ b/_test/data/spec-02-03.data
@@ -0,0 +1,8 @@
+american:
+ - Boston Red Sox
+ - Detroit Tigers
+ - New York Yankees
+national:
+ - New York Mets
+ - Chicago Cubs
+ - Atlanta Braves
diff --git a/_test/data/spec-02-03.structure b/_test/data/spec-02-03.structure
new file mode 100644
index 0000000..25de5d2
--- /dev/null
+++ b/_test/data/spec-02-03.structure
@@ -0,0 +1 @@
+[(True, [True, True, True]), (True, [True, True, True])]
diff --git a/_test/data/spec-02-03.tokens b/_test/data/spec-02-03.tokens
new file mode 100644
index 0000000..89815f2
--- /dev/null
+++ b/_test/data/spec-02-03.tokens
@@ -0,0 +1,4 @@
+{{
+? _ : [[ , _ , _ , _ ]}
+? _ : [[ , _ , _ , _ ]}
+]}
diff --git a/_test/data/spec-02-04.data b/_test/data/spec-02-04.data
new file mode 100644
index 0000000..430f6b3
--- /dev/null
+++ b/_test/data/spec-02-04.data
@@ -0,0 +1,8 @@
+-
+ name: Mark McGwire
+ hr: 65
+ avg: 0.278
+-
+ name: Sammy Sosa
+ hr: 63
+ avg: 0.288
diff --git a/_test/data/spec-02-04.structure b/_test/data/spec-02-04.structure
new file mode 100644
index 0000000..e7b526c
--- /dev/null
+++ b/_test/data/spec-02-04.structure
@@ -0,0 +1,4 @@
+[
+ [(True, True), (True, True), (True, True)],
+ [(True, True), (True, True), (True, True)],
+]
diff --git a/_test/data/spec-02-04.tokens b/_test/data/spec-02-04.tokens
new file mode 100644
index 0000000..9cb9815
--- /dev/null
+++ b/_test/data/spec-02-04.tokens
@@ -0,0 +1,4 @@
+[[
+, {{ ? _ : _ ? _ : _ ? _ : _ ]}
+, {{ ? _ : _ ? _ : _ ? _ : _ ]}
+]}
diff --git a/_test/data/spec-02-05.data b/_test/data/spec-02-05.data
new file mode 100644
index 0000000..cdd7770
--- /dev/null
+++ b/_test/data/spec-02-05.data
@@ -0,0 +1,3 @@
+- [name , hr, avg ]
+- [Mark McGwire, 65, 0.278]
+- [Sammy Sosa , 63, 0.288]
diff --git a/_test/data/spec-02-05.structure b/_test/data/spec-02-05.structure
new file mode 100644
index 0000000..e06b75a
--- /dev/null
+++ b/_test/data/spec-02-05.structure
@@ -0,0 +1,5 @@
+[
+ [True, True, True],
+ [True, True, True],
+ [True, True, True],
+]
diff --git a/_test/data/spec-02-05.tokens b/_test/data/spec-02-05.tokens
new file mode 100644
index 0000000..3f6f1ab
--- /dev/null
+++ b/_test/data/spec-02-05.tokens
@@ -0,0 +1,5 @@
+[[
+, [ _ , _ , _ ]
+, [ _ , _ , _ ]
+, [ _ , _ , _ ]
+]}
diff --git a/_test/data/spec-02-06.data b/_test/data/spec-02-06.data
new file mode 100644
index 0000000..7a957b2
--- /dev/null
+++ b/_test/data/spec-02-06.data
@@ -0,0 +1,5 @@
+Mark McGwire: {hr: 65, avg: 0.278}
+Sammy Sosa: {
+ hr: 63,
+ avg: 0.288
+ }
diff --git a/_test/data/spec-02-06.structure b/_test/data/spec-02-06.structure
new file mode 100644
index 0000000..3ef0f4b
--- /dev/null
+++ b/_test/data/spec-02-06.structure
@@ -0,0 +1,4 @@
+[
+ (True, [(True, True), (True, True)]),
+ (True, [(True, True), (True, True)]),
+]
diff --git a/_test/data/spec-02-06.tokens b/_test/data/spec-02-06.tokens
new file mode 100644
index 0000000..a1a5eef
--- /dev/null
+++ b/_test/data/spec-02-06.tokens
@@ -0,0 +1,4 @@
+{{
+? _ : { ? _ : _ , ? _ : _ }
+? _ : { ? _ : _ , ? _ : _ }
+]}
diff --git a/_test/data/spec-02-07.data b/_test/data/spec-02-07.data
new file mode 100644
index 0000000..bc711d5
--- /dev/null
+++ b/_test/data/spec-02-07.data
@@ -0,0 +1,10 @@
+# Ranking of 1998 home runs
+---
+- Mark McGwire
+- Sammy Sosa
+- Ken Griffey
+
+# Team ranking
+---
+- Chicago Cubs
+- St Louis Cardinals
diff --git a/_test/data/spec-02-07.structure b/_test/data/spec-02-07.structure
new file mode 100644
index 0000000..c5d72a3
--- /dev/null
+++ b/_test/data/spec-02-07.structure
@@ -0,0 +1,4 @@
+[
+[True, True, True],
+[True, True],
+]
diff --git a/_test/data/spec-02-07.tokens b/_test/data/spec-02-07.tokens
new file mode 100644
index 0000000..ed48883
--- /dev/null
+++ b/_test/data/spec-02-07.tokens
@@ -0,0 +1,12 @@
+---
+[[
+, _
+, _
+, _
+]}
+
+---
+[[
+, _
+, _
+]}
diff --git a/_test/data/spec-02-08.data b/_test/data/spec-02-08.data
new file mode 100644
index 0000000..05e102d
--- /dev/null
+++ b/_test/data/spec-02-08.data
@@ -0,0 +1,10 @@
+---
+time: 20:03:20
+player: Sammy Sosa
+action: strike (miss)
+...
+---
+time: 20:03:47
+player: Sammy Sosa
+action: grand slam
+...
diff --git a/_test/data/spec-02-08.structure b/_test/data/spec-02-08.structure
new file mode 100644
index 0000000..24cff73
--- /dev/null
+++ b/_test/data/spec-02-08.structure
@@ -0,0 +1,4 @@
+[
+[(True, True), (True, True), (True, True)],
+[(True, True), (True, True), (True, True)],
+]
diff --git a/_test/data/spec-02-08.tokens b/_test/data/spec-02-08.tokens
new file mode 100644
index 0000000..7d2c03d
--- /dev/null
+++ b/_test/data/spec-02-08.tokens
@@ -0,0 +1,15 @@
+---
+{{
+? _ : _
+? _ : _
+? _ : _
+]}
+...
+
+---
+{{
+? _ : _
+? _ : _
+? _ : _
+]}
+...
diff --git a/_test/data/spec-02-09.data b/_test/data/spec-02-09.data
new file mode 100644
index 0000000..e264180
--- /dev/null
+++ b/_test/data/spec-02-09.data
@@ -0,0 +1,8 @@
+---
+hr: # 1998 hr ranking
+ - Mark McGwire
+ - Sammy Sosa
+rbi:
+ # 1998 rbi ranking
+ - Sammy Sosa
+ - Ken Griffey
diff --git a/_test/data/spec-02-09.structure b/_test/data/spec-02-09.structure
new file mode 100644
index 0000000..b4c9914
--- /dev/null
+++ b/_test/data/spec-02-09.structure
@@ -0,0 +1 @@
+[(True, [True, True]), (True, [True, True])]
diff --git a/_test/data/spec-02-09.tokens b/_test/data/spec-02-09.tokens
new file mode 100644
index 0000000..b2ec10e
--- /dev/null
+++ b/_test/data/spec-02-09.tokens
@@ -0,0 +1,5 @@
+---
+{{
+? _ : [[ , _ , _ ]}
+? _ : [[ , _ , _ ]}
+]}
diff --git a/_test/data/spec-02-10.data b/_test/data/spec-02-10.data
new file mode 100644
index 0000000..61808f6
--- /dev/null
+++ b/_test/data/spec-02-10.data
@@ -0,0 +1,8 @@
+---
+hr:
+ - Mark McGwire
+ # Following node labeled SS
+ - &SS Sammy Sosa
+rbi:
+ - *SS # Subsequent occurrence
+ - Ken Griffey
diff --git a/_test/data/spec-02-10.structure b/_test/data/spec-02-10.structure
new file mode 100644
index 0000000..ff8f4c3
--- /dev/null
+++ b/_test/data/spec-02-10.structure
@@ -0,0 +1 @@
+[(True, [True, True]), (True, ['*', True])]
diff --git a/_test/data/spec-02-10.tokens b/_test/data/spec-02-10.tokens
new file mode 100644
index 0000000..26caa2b
--- /dev/null
+++ b/_test/data/spec-02-10.tokens
@@ -0,0 +1,5 @@
+---
+{{
+? _ : [[ , _ , & _ ]}
+? _ : [[ , * , _ ]}
+]}
diff --git a/_test/data/spec-02-11.code b/_test/data/spec-02-11.code
new file mode 100644
index 0000000..6e02325
--- /dev/null
+++ b/_test/data/spec-02-11.code
@@ -0,0 +1,10 @@
+{
+('Detroit Tigers', 'Chicago cubs'): [datetime.date(2001, 7, 23)],
+
+('New York Yankees', 'Atlanta Braves'):
+ [datetime.date(2001, 7, 2),
+ datetime.date(2001, 8, 12),
+ datetime.date(2001, 8, 14)]
+}
+
+
diff --git a/_test/data/spec-02-11.data b/_test/data/spec-02-11.data
new file mode 100644
index 0000000..9123ce2
--- /dev/null
+++ b/_test/data/spec-02-11.data
@@ -0,0 +1,9 @@
+? - Detroit Tigers
+ - Chicago cubs
+:
+ - 2001-07-23
+
+? [ New York Yankees,
+ Atlanta Braves ]
+: [ 2001-07-02, 2001-08-12,
+ 2001-08-14 ]
diff --git a/_test/data/spec-02-11.structure b/_test/data/spec-02-11.structure
new file mode 100644
index 0000000..3d8f1ff
--- /dev/null
+++ b/_test/data/spec-02-11.structure
@@ -0,0 +1,4 @@
+[
+([True, True], [True]),
+([True, True], [True, True, True]),
+]
diff --git a/_test/data/spec-02-11.tokens b/_test/data/spec-02-11.tokens
new file mode 100644
index 0000000..fe24203
--- /dev/null
+++ b/_test/data/spec-02-11.tokens
@@ -0,0 +1,6 @@
+{{
+? [[ , _ , _ ]}
+: [[ , _ ]}
+? [ _ , _ ]
+: [ _ , _ , _ ]
+]}
diff --git a/_test/data/spec-02-12.data b/_test/data/spec-02-12.data
new file mode 100644
index 0000000..1fc33f9
--- /dev/null
+++ b/_test/data/spec-02-12.data
@@ -0,0 +1,8 @@
+---
+# products purchased
+- item : Super Hoop
+ quantity: 1
+- item : Basketball
+ quantity: 4
+- item : Big Shoes
+ quantity: 1
diff --git a/_test/data/spec-02-12.structure b/_test/data/spec-02-12.structure
new file mode 100644
index 0000000..e9c5359
--- /dev/null
+++ b/_test/data/spec-02-12.structure
@@ -0,0 +1,5 @@
+[
+[(True, True), (True, True)],
+[(True, True), (True, True)],
+[(True, True), (True, True)],
+]
diff --git a/_test/data/spec-02-12.tokens b/_test/data/spec-02-12.tokens
new file mode 100644
index 0000000..ea21e50
--- /dev/null
+++ b/_test/data/spec-02-12.tokens
@@ -0,0 +1,6 @@
+---
+[[
+, {{ ? _ : _ ? _ : _ ]}
+, {{ ? _ : _ ? _ : _ ]}
+, {{ ? _ : _ ? _ : _ ]}
+]}
diff --git a/_test/data/spec-02-13.data b/_test/data/spec-02-13.data
new file mode 100644
index 0000000..13fb656
--- /dev/null
+++ b/_test/data/spec-02-13.data
@@ -0,0 +1,4 @@
+# ASCII Art
+--- |
+ \//||\/||
+ // || ||__
diff --git a/_test/data/spec-02-13.structure b/_test/data/spec-02-13.structure
new file mode 100644
index 0000000..0ca9514
--- /dev/null
+++ b/_test/data/spec-02-13.structure
@@ -0,0 +1 @@
+True
diff --git a/_test/data/spec-02-13.tokens b/_test/data/spec-02-13.tokens
new file mode 100644
index 0000000..7456c05
--- /dev/null
+++ b/_test/data/spec-02-13.tokens
@@ -0,0 +1 @@
+--- _
diff --git a/_test/data/spec-02-14.data b/_test/data/spec-02-14.data
new file mode 100644
index 0000000..59943de
--- /dev/null
+++ b/_test/data/spec-02-14.data
@@ -0,0 +1,4 @@
+---
+ Mark McGwire's
+ year was crippled
+ by a knee injury.
diff --git a/_test/data/spec-02-14.structure b/_test/data/spec-02-14.structure
new file mode 100644
index 0000000..0ca9514
--- /dev/null
+++ b/_test/data/spec-02-14.structure
@@ -0,0 +1 @@
+True
diff --git a/_test/data/spec-02-14.tokens b/_test/data/spec-02-14.tokens
new file mode 100644
index 0000000..7456c05
--- /dev/null
+++ b/_test/data/spec-02-14.tokens
@@ -0,0 +1 @@
+--- _
diff --git a/_test/data/spec-02-15.data b/_test/data/spec-02-15.data
new file mode 100644
index 0000000..80b89a6
--- /dev/null
+++ b/_test/data/spec-02-15.data
@@ -0,0 +1,8 @@
+>
+ Sammy Sosa completed another
+ fine season with great stats.
+
+ 63 Home Runs
+ 0.288 Batting Average
+
+ What a year!
diff --git a/_test/data/spec-02-15.structure b/_test/data/spec-02-15.structure
new file mode 100644
index 0000000..0ca9514
--- /dev/null
+++ b/_test/data/spec-02-15.structure
@@ -0,0 +1 @@
+True
diff --git a/_test/data/spec-02-15.tokens b/_test/data/spec-02-15.tokens
new file mode 100644
index 0000000..31354ec
--- /dev/null
+++ b/_test/data/spec-02-15.tokens
@@ -0,0 +1 @@
+_
diff --git a/_test/data/spec-02-16.data b/_test/data/spec-02-16.data
new file mode 100644
index 0000000..9f66d88
--- /dev/null
+++ b/_test/data/spec-02-16.data
@@ -0,0 +1,7 @@
+name: Mark McGwire
+accomplishment: >
+ Mark set a major league
+ home run record in 1998.
+stats: |
+ 65 Home Runs
+ 0.278 Batting Average
diff --git a/_test/data/spec-02-16.structure b/_test/data/spec-02-16.structure
new file mode 100644
index 0000000..aba1ced
--- /dev/null
+++ b/_test/data/spec-02-16.structure
@@ -0,0 +1 @@
+[(True, True), (True, True), (True, True)]
diff --git a/_test/data/spec-02-16.tokens b/_test/data/spec-02-16.tokens
new file mode 100644
index 0000000..e4e381b
--- /dev/null
+++ b/_test/data/spec-02-16.tokens
@@ -0,0 +1,5 @@
+{{
+? _ : _
+? _ : _
+? _ : _
+]}
diff --git a/_test/data/spec-02-17.data b/_test/data/spec-02-17.data
new file mode 100644
index 0000000..b2870c5
--- /dev/null
+++ b/_test/data/spec-02-17.data
@@ -0,0 +1,7 @@
+unicode: "Sosa did fine.\u263A"
+control: "\b1998\t1999\t2000\n"
+hexesc: "\x13\x10 is \r\n"
+
+single: '"Howdy!" he cried.'
+quoted: ' # not a ''comment''.'
+tie-fighter: '|\-*-/|'
diff --git a/_test/data/spec-02-17.structure b/_test/data/spec-02-17.structure
new file mode 100644
index 0000000..933646d
--- /dev/null
+++ b/_test/data/spec-02-17.structure
@@ -0,0 +1 @@
+[(True, True), (True, True), (True, True), (True, True), (True, True), (True, True)]
diff --git a/_test/data/spec-02-17.tokens b/_test/data/spec-02-17.tokens
new file mode 100644
index 0000000..db65540
--- /dev/null
+++ b/_test/data/spec-02-17.tokens
@@ -0,0 +1,8 @@
+{{
+? _ : _
+? _ : _
+? _ : _
+? _ : _
+? _ : _
+? _ : _
+]}
diff --git a/_test/data/spec-02-18.data b/_test/data/spec-02-18.data
new file mode 100644
index 0000000..e0a8bfa
--- /dev/null
+++ b/_test/data/spec-02-18.data
@@ -0,0 +1,6 @@
+plain:
+ This unquoted scalar
+ spans many lines.
+
+quoted: "So does this
+ quoted scalar.\n"
diff --git a/_test/data/spec-02-18.structure b/_test/data/spec-02-18.structure
new file mode 100644
index 0000000..0ca4991
--- /dev/null
+++ b/_test/data/spec-02-18.structure
@@ -0,0 +1 @@
+[(True, True), (True, True)]
diff --git a/_test/data/spec-02-18.tokens b/_test/data/spec-02-18.tokens
new file mode 100644
index 0000000..83b31dc
--- /dev/null
+++ b/_test/data/spec-02-18.tokens
@@ -0,0 +1,4 @@
+{{
+? _ : _
+? _ : _
+]}
diff --git a/_test/data/spec-02-19.data b/_test/data/spec-02-19.data
new file mode 100644
index 0000000..bf69de6
--- /dev/null
+++ b/_test/data/spec-02-19.data
@@ -0,0 +1,5 @@
+canonical: 12345
+decimal: +12,345
+sexagesimal: 3:25:45
+octal: 014
+hexadecimal: 0xC
diff --git a/_test/data/spec-02-19.structure b/_test/data/spec-02-19.structure
new file mode 100644
index 0000000..48ca99d
--- /dev/null
+++ b/_test/data/spec-02-19.structure
@@ -0,0 +1 @@
+[(True, True), (True, True), (True, True), (True, True), (True, True)]
diff --git a/_test/data/spec-02-19.tokens b/_test/data/spec-02-19.tokens
new file mode 100644
index 0000000..5bda68f
--- /dev/null
+++ b/_test/data/spec-02-19.tokens
@@ -0,0 +1,7 @@
+{{
+? _ : _
+? _ : _
+? _ : _
+? _ : _
+? _ : _
+]}
diff --git a/_test/data/spec-02-20.data b/_test/data/spec-02-20.data
new file mode 100644
index 0000000..1d4897f
--- /dev/null
+++ b/_test/data/spec-02-20.data
@@ -0,0 +1,6 @@
+canonical: 1.23015e+3
+exponential: 12.3015e+02
+sexagesimal: 20:30.15
+fixed: 1,230.15
+negative infinity: -.inf
+not a number: .NaN
diff --git a/_test/data/spec-02-20.structure b/_test/data/spec-02-20.structure
new file mode 100644
index 0000000..933646d
--- /dev/null
+++ b/_test/data/spec-02-20.structure
@@ -0,0 +1 @@
+[(True, True), (True, True), (True, True), (True, True), (True, True), (True, True)]
diff --git a/_test/data/spec-02-20.tokens b/_test/data/spec-02-20.tokens
new file mode 100644
index 0000000..db65540
--- /dev/null
+++ b/_test/data/spec-02-20.tokens
@@ -0,0 +1,8 @@
+{{
+? _ : _
+? _ : _
+? _ : _
+? _ : _
+? _ : _
+? _ : _
+]}
diff --git a/_test/data/spec-02-21.data b/_test/data/spec-02-21.data
new file mode 100644
index 0000000..dec6a56
--- /dev/null
+++ b/_test/data/spec-02-21.data
@@ -0,0 +1,4 @@
+null: ~
+true: y
+false: n
+string: '12345'
diff --git a/_test/data/spec-02-21.structure b/_test/data/spec-02-21.structure
new file mode 100644
index 0000000..021635f
--- /dev/null
+++ b/_test/data/spec-02-21.structure
@@ -0,0 +1 @@
+[(True, True), (True, True), (True, True), (True, True)]
diff --git a/_test/data/spec-02-21.tokens b/_test/data/spec-02-21.tokens
new file mode 100644
index 0000000..aeccbaf
--- /dev/null
+++ b/_test/data/spec-02-21.tokens
@@ -0,0 +1,6 @@
+{{
+? _ : _
+? _ : _
+? _ : _
+? _ : _
+]}
diff --git a/_test/data/spec-02-22.data b/_test/data/spec-02-22.data
new file mode 100644
index 0000000..aaac185
--- /dev/null
+++ b/_test/data/spec-02-22.data
@@ -0,0 +1,4 @@
+canonical: 2001-12-15T02:59:43.1Z
+iso8601: 2001-12-14t21:59:43.10-05:00
+spaced: 2001-12-14 21:59:43.10 -5
+date: 2002-12-14
diff --git a/_test/data/spec-02-22.structure b/_test/data/spec-02-22.structure
new file mode 100644
index 0000000..021635f
--- /dev/null
+++ b/_test/data/spec-02-22.structure
@@ -0,0 +1 @@
+[(True, True), (True, True), (True, True), (True, True)]
diff --git a/_test/data/spec-02-22.tokens b/_test/data/spec-02-22.tokens
new file mode 100644
index 0000000..aeccbaf
--- /dev/null
+++ b/_test/data/spec-02-22.tokens
@@ -0,0 +1,6 @@
+{{
+? _ : _
+? _ : _
+? _ : _
+? _ : _
+]}
diff --git a/_test/data/spec-02-23.data b/_test/data/spec-02-23.data
new file mode 100644
index 0000000..5dbd992
--- /dev/null
+++ b/_test/data/spec-02-23.data
@@ -0,0 +1,13 @@
+---
+not-date: !!str 2002-04-28
+
+picture: !!binary |
+ R0lGODlhDAAMAIQAAP//9/X
+ 17unp5WZmZgAAAOfn515eXv
+ Pz7Y6OjuDg4J+fn5OTk6enp
+ 56enmleECcgggoBADs=
+
+application specific tag: !something |
+ The semantics of the tag
+ above may be different for
+ different documents.
diff --git a/_test/data/spec-02-23.structure b/_test/data/spec-02-23.structure
new file mode 100644
index 0000000..aba1ced
--- /dev/null
+++ b/_test/data/spec-02-23.structure
@@ -0,0 +1 @@
+[(True, True), (True, True), (True, True)]
diff --git a/_test/data/spec-02-23.tokens b/_test/data/spec-02-23.tokens
new file mode 100644
index 0000000..9ac54aa
--- /dev/null
+++ b/_test/data/spec-02-23.tokens
@@ -0,0 +1,6 @@
+---
+{{
+? _ : ! _
+? _ : ! _
+? _ : ! _
+]}
diff --git a/_test/data/spec-02-24.data b/_test/data/spec-02-24.data
new file mode 100644
index 0000000..1180757
--- /dev/null
+++ b/_test/data/spec-02-24.data
@@ -0,0 +1,14 @@
+%TAG ! tag:clarkevans.com,2002:
+--- !shape
+ # Use the ! handle for presenting
+ # tag:clarkevans.com,2002:circle
+- !circle
+ center: &ORIGIN {x: 73, y: 129}
+ radius: 7
+- !line
+ start: *ORIGIN
+ finish: { x: 89, y: 102 }
+- !label
+ start: *ORIGIN
+ color: 0xFFEEBB
+ text: Pretty vector drawing.
diff --git a/_test/data/spec-02-24.structure b/_test/data/spec-02-24.structure
new file mode 100644
index 0000000..a800729
--- /dev/null
+++ b/_test/data/spec-02-24.structure
@@ -0,0 +1,5 @@
+[
+[(True, [(True, True), (True, True)]), (True, True)],
+[(True, '*'), (True, [(True, True), (True, True)])],
+[(True, '*'), (True, True), (True, True)],
+]
diff --git a/_test/data/spec-02-24.tokens b/_test/data/spec-02-24.tokens
new file mode 100644
index 0000000..039c385
--- /dev/null
+++ b/_test/data/spec-02-24.tokens
@@ -0,0 +1,20 @@
+%
+--- !
+[[
+, !
+ {{
+ ? _ : & { ? _ : _ , ? _ : _ }
+ ? _ : _
+ ]}
+, !
+ {{
+ ? _ : *
+ ? _ : { ? _ : _ , ? _ : _ }
+ ]}
+, !
+ {{
+ ? _ : *
+ ? _ : _
+ ? _ : _
+ ]}
+]}
diff --git a/_test/data/spec-02-25.data b/_test/data/spec-02-25.data
new file mode 100644
index 0000000..769ac31
--- /dev/null
+++ b/_test/data/spec-02-25.data
@@ -0,0 +1,7 @@
+# sets are represented as a
+# mapping where each key is
+# associated with the empty string
+--- !!set
+? Mark McGwire
+? Sammy Sosa
+? Ken Griff
diff --git a/_test/data/spec-02-25.structure b/_test/data/spec-02-25.structure
new file mode 100644
index 0000000..0b40e61
--- /dev/null
+++ b/_test/data/spec-02-25.structure
@@ -0,0 +1 @@
+[(True, None), (True, None), (True, None)]
diff --git a/_test/data/spec-02-25.tokens b/_test/data/spec-02-25.tokens
new file mode 100644
index 0000000..b700236
--- /dev/null
+++ b/_test/data/spec-02-25.tokens
@@ -0,0 +1,6 @@
+--- !
+{{
+? _
+? _
+? _
+]}
diff --git a/_test/data/spec-02-26.data b/_test/data/spec-02-26.data
new file mode 100644
index 0000000..3143763
--- /dev/null
+++ b/_test/data/spec-02-26.data
@@ -0,0 +1,7 @@
+# ordered maps are represented as
+# a sequence of mappings, with
+# each mapping having one key
+--- !!omap
+- Mark McGwire: 65
+- Sammy Sosa: 63
+- Ken Griffy: 58
diff --git a/_test/data/spec-02-26.structure b/_test/data/spec-02-26.structure
new file mode 100644
index 0000000..cf429b9
--- /dev/null
+++ b/_test/data/spec-02-26.structure
@@ -0,0 +1,5 @@
+[
+[(True, True)],
+[(True, True)],
+[(True, True)],
+]
diff --git a/_test/data/spec-02-26.tokens b/_test/data/spec-02-26.tokens
new file mode 100644
index 0000000..7bee492
--- /dev/null
+++ b/_test/data/spec-02-26.tokens
@@ -0,0 +1,6 @@
+--- !
+[[
+, {{ ? _ : _ ]}
+, {{ ? _ : _ ]}
+, {{ ? _ : _ ]}
+]}
diff --git a/_test/data/spec-02-27.data b/_test/data/spec-02-27.data
new file mode 100644
index 0000000..4625739
--- /dev/null
+++ b/_test/data/spec-02-27.data
@@ -0,0 +1,29 @@
+--- !<tag:clarkevans.com,2002:invoice>
+invoice: 34843
+date : 2001-01-23
+bill-to: &id001
+ given : Chris
+ family : Dumars
+ address:
+ lines: |
+ 458 Walkman Dr.
+ Suite #292
+ city : Royal Oak
+ state : MI
+ postal : 48046
+ship-to: *id001
+product:
+ - sku : BL394D
+ quantity : 4
+ description : Basketball
+ price : 450.00
+ - sku : BL4438H
+ quantity : 1
+ description : Super Hoop
+ price : 2392.00
+tax : 251.42
+total: 4443.52
+comments:
+ Late afternoon is best.
+ Backup contact is Nancy
+ Billsmer @ 338-4338.
diff --git a/_test/data/spec-02-27.structure b/_test/data/spec-02-27.structure
new file mode 100644
index 0000000..a2113b9
--- /dev/null
+++ b/_test/data/spec-02-27.structure
@@ -0,0 +1,17 @@
+[
+(True, True),
+(True, True),
+(True, [
+ (True, True),
+ (True, True),
+ (True, [(True, True), (True, True), (True, True), (True, True)]),
+ ]),
+(True, '*'),
+(True, [
+ [(True, True), (True, True), (True, True), (True, True)],
+ [(True, True), (True, True), (True, True), (True, True)],
+ ]),
+(True, True),
+(True, True),
+(True, True),
+]
diff --git a/_test/data/spec-02-27.tokens b/_test/data/spec-02-27.tokens
new file mode 100644
index 0000000..2dc1c25
--- /dev/null
+++ b/_test/data/spec-02-27.tokens
@@ -0,0 +1,20 @@
+--- !
+{{
+? _ : _
+? _ : _
+? _ : &
+ {{
+ ? _ : _
+ ? _ : _
+ ? _ : {{ ? _ : _ ? _ : _ ? _ : _ ? _ : _ ]}
+ ]}
+? _ : *
+? _ :
+ [[
+ , {{ ? _ : _ ? _ : _ ? _ : _ ? _ : _ ]}
+ , {{ ? _ : _ ? _ : _ ? _ : _ ? _ : _ ]}
+ ]}
+? _ : _
+? _ : _
+? _ : _
+]}
diff --git a/_test/data/spec-02-28.data b/_test/data/spec-02-28.data
new file mode 100644
index 0000000..a5c8dc8
--- /dev/null
+++ b/_test/data/spec-02-28.data
@@ -0,0 +1,26 @@
+---
+Time: 2001-11-23 15:01:42 -5
+User: ed
+Warning:
+ This is an error message
+ for the log file
+---
+Time: 2001-11-23 15:02:31 -5
+User: ed
+Warning:
+ A slightly different error
+ message.
+---
+Date: 2001-11-23 15:03:17 -5
+User: ed
+Fatal:
+ Unknown variable "bar"
+Stack:
+ - file: TopClass.py
+ line: 23
+ code: |
+ x = MoreObject("345\n")
+ - file: MoreClass.py
+ line: 58
+ code: |-
+ foo = bar
diff --git a/_test/data/spec-02-28.structure b/_test/data/spec-02-28.structure
new file mode 100644
index 0000000..8ec0b56
--- /dev/null
+++ b/_test/data/spec-02-28.structure
@@ -0,0 +1,10 @@
+[
+[(True, True), (True, True), (True, True)],
+[(True, True), (True, True), (True, True)],
+[(True, True), (True, True), (True, True),
+(True, [
+ [(True, True), (True, True), (True, True)],
+ [(True, True), (True, True), (True, True)],
+ ]),
+]
+]
diff --git a/_test/data/spec-02-28.tokens b/_test/data/spec-02-28.tokens
new file mode 100644
index 0000000..8d5e1bc
--- /dev/null
+++ b/_test/data/spec-02-28.tokens
@@ -0,0 +1,23 @@
+---
+{{
+? _ : _
+? _ : _
+? _ : _
+]}
+---
+{{
+? _ : _
+? _ : _
+? _ : _
+]}
+---
+{{
+? _ : _
+? _ : _
+? _ : _
+? _ :
+ [[
+ , {{ ? _ : _ ? _ : _ ? _ : _ ]}
+ , {{ ? _ : _ ? _ : _ ? _ : _ ]}
+ ]}
+]}
diff --git a/_test/data/spec-05-01-utf16be.data b/_test/data/spec-05-01-utf16be.data
new file mode 100644
index 0000000..3525062
--- /dev/null
+++ b/_test/data/spec-05-01-utf16be.data
Binary files differ
diff --git a/_test/data/spec-05-01-utf16be.empty b/_test/data/spec-05-01-utf16be.empty
new file mode 100644
index 0000000..bfffa8b
--- /dev/null
+++ b/_test/data/spec-05-01-utf16be.empty
@@ -0,0 +1,2 @@
+# This stream contains no
+# documents, only comments.
diff --git a/_test/data/spec-05-01-utf16le.data b/_test/data/spec-05-01-utf16le.data
new file mode 100644
index 0000000..0823f74
--- /dev/null
+++ b/_test/data/spec-05-01-utf16le.data
Binary files differ
diff --git a/_test/data/spec-05-01-utf16le.empty b/_test/data/spec-05-01-utf16le.empty
new file mode 100644
index 0000000..bfffa8b
--- /dev/null
+++ b/_test/data/spec-05-01-utf16le.empty
@@ -0,0 +1,2 @@
+# This stream contains no
+# documents, only comments.
diff --git a/_test/data/spec-05-01-utf8.data b/_test/data/spec-05-01-utf8.data
new file mode 100644
index 0000000..780d25b
--- /dev/null
+++ b/_test/data/spec-05-01-utf8.data
@@ -0,0 +1 @@
+# Comment only.
diff --git a/_test/data/spec-05-01-utf8.empty b/_test/data/spec-05-01-utf8.empty
new file mode 100644
index 0000000..bfffa8b
--- /dev/null
+++ b/_test/data/spec-05-01-utf8.empty
@@ -0,0 +1,2 @@
+# This stream contains no
+# documents, only comments.
diff --git a/_test/data/spec-05-02-utf16be.data b/_test/data/spec-05-02-utf16be.data
new file mode 100644
index 0000000..5ebbb04
--- /dev/null
+++ b/_test/data/spec-05-02-utf16be.data
Binary files differ
diff --git a/_test/data/spec-05-02-utf16be.error b/_test/data/spec-05-02-utf16be.error
new file mode 100644
index 0000000..1df3616
--- /dev/null
+++ b/_test/data/spec-05-02-utf16be.error
@@ -0,0 +1,3 @@
+ERROR:
+ A BOM must not appear
+ inside a document.
diff --git a/_test/data/spec-05-02-utf16le.data b/_test/data/spec-05-02-utf16le.data
new file mode 100644
index 0000000..0cd90a2
--- /dev/null
+++ b/_test/data/spec-05-02-utf16le.data
Binary files differ
diff --git a/_test/data/spec-05-02-utf16le.error b/_test/data/spec-05-02-utf16le.error
new file mode 100644
index 0000000..1df3616
--- /dev/null
+++ b/_test/data/spec-05-02-utf16le.error
@@ -0,0 +1,3 @@
+ERROR:
+ A BOM must not appear
+ inside a document.
diff --git a/_test/data/spec-05-02-utf8.data b/_test/data/spec-05-02-utf8.data
new file mode 100644
index 0000000..fb74866
--- /dev/null
+++ b/_test/data/spec-05-02-utf8.data
@@ -0,0 +1,3 @@
+# Invalid use of BOM
+# inside a
+# document.
diff --git a/_test/data/spec-05-02-utf8.error b/_test/data/spec-05-02-utf8.error
new file mode 100644
index 0000000..1df3616
--- /dev/null
+++ b/_test/data/spec-05-02-utf8.error
@@ -0,0 +1,3 @@
+ERROR:
+ A BOM must not appear
+ inside a document.
diff --git a/_test/data/spec-05-03.canonical b/_test/data/spec-05-03.canonical
new file mode 100644
index 0000000..a143a73
--- /dev/null
+++ b/_test/data/spec-05-03.canonical
@@ -0,0 +1,14 @@
+%YAML 1.1
+---
+!!map {
+ ? !!str "sequence"
+ : !!seq [
+ !!str "one", !!str "two"
+ ],
+ ? !!str "mapping"
+ : !!map {
+ ? !!str "sky" : !!str "blue",
+# ? !!str "sea" : !!str "green",
+ ? !!map { ? !!str "sea" : !!str "green" } : !!null "",
+ }
+}
diff --git a/_test/data/spec-05-03.data b/_test/data/spec-05-03.data
new file mode 100644
index 0000000..4661f33
--- /dev/null
+++ b/_test/data/spec-05-03.data
@@ -0,0 +1,7 @@
+sequence:
+- one
+- two
+mapping:
+ ? sky
+ : blue
+ ? sea : green
diff --git a/_test/data/spec-05-04.canonical b/_test/data/spec-05-04.canonical
new file mode 100644
index 0000000..00c9723
--- /dev/null
+++ b/_test/data/spec-05-04.canonical
@@ -0,0 +1,13 @@
+%YAML 1.1
+---
+!!map {
+ ? !!str "sequence"
+ : !!seq [
+ !!str "one", !!str "two"
+ ],
+ ? !!str "mapping"
+ : !!map {
+ ? !!str "sky" : !!str "blue",
+ ? !!str "sea" : !!str "green",
+ }
+}
diff --git a/_test/data/spec-05-04.data b/_test/data/spec-05-04.data
new file mode 100644
index 0000000..df33847
--- /dev/null
+++ b/_test/data/spec-05-04.data
@@ -0,0 +1,2 @@
+sequence: [ one, two, ]
+mapping: { sky: blue, sea: green }
diff --git a/_test/data/spec-05-05.data b/_test/data/spec-05-05.data
new file mode 100644
index 0000000..62524c0
--- /dev/null
+++ b/_test/data/spec-05-05.data
@@ -0,0 +1 @@
+# Comment only.
diff --git a/_test/data/spec-05-05.empty b/_test/data/spec-05-05.empty
new file mode 100644
index 0000000..bfffa8b
--- /dev/null
+++ b/_test/data/spec-05-05.empty
@@ -0,0 +1,2 @@
+# This stream contains no
+# documents, only comments.
diff --git a/_test/data/spec-05-06.canonical b/_test/data/spec-05-06.canonical
new file mode 100644
index 0000000..4f30c11
--- /dev/null
+++ b/_test/data/spec-05-06.canonical
@@ -0,0 +1,8 @@
+%YAML 1.1
+---
+!!map {
+ ? !!str "anchored"
+ : &A1 !local "value",
+ ? !!str "alias"
+ : *A1,
+}
diff --git a/_test/data/spec-05-06.data b/_test/data/spec-05-06.data
new file mode 100644
index 0000000..7a1f9b3
--- /dev/null
+++ b/_test/data/spec-05-06.data
@@ -0,0 +1,2 @@
+anchored: !local &anchor value
+alias: *anchor
diff --git a/_test/data/spec-05-07.canonical b/_test/data/spec-05-07.canonical
new file mode 100644
index 0000000..dc3732a
--- /dev/null
+++ b/_test/data/spec-05-07.canonical
@@ -0,0 +1,8 @@
+%YAML 1.1
+---
+!!map {
+ ? !!str "literal"
+ : !!str "text\n",
+ ? !!str "folded"
+ : !!str "text\n",
+}
diff --git a/_test/data/spec-05-07.data b/_test/data/spec-05-07.data
new file mode 100644
index 0000000..97eb3a3
--- /dev/null
+++ b/_test/data/spec-05-07.data
@@ -0,0 +1,4 @@
+literal: |
+ text
+folded: >
+ text
diff --git a/_test/data/spec-05-08.canonical b/_test/data/spec-05-08.canonical
new file mode 100644
index 0000000..610bd68
--- /dev/null
+++ b/_test/data/spec-05-08.canonical
@@ -0,0 +1,8 @@
+%YAML 1.1
+---
+!!map {
+ ? !!str "single"
+ : !!str "text",
+ ? !!str "double"
+ : !!str "text",
+}
diff --git a/_test/data/spec-05-08.data b/_test/data/spec-05-08.data
new file mode 100644
index 0000000..04ebf69
--- /dev/null
+++ b/_test/data/spec-05-08.data
@@ -0,0 +1,2 @@
+single: 'text'
+double: "text"
diff --git a/_test/data/spec-05-09.canonical b/_test/data/spec-05-09.canonical
new file mode 100644
index 0000000..597e3de
--- /dev/null
+++ b/_test/data/spec-05-09.canonical
@@ -0,0 +1,3 @@
+%YAML 1.1
+---
+!!str "text"
diff --git a/_test/data/spec-05-09.data b/_test/data/spec-05-09.data
new file mode 100644
index 0000000..a43431b
--- /dev/null
+++ b/_test/data/spec-05-09.data
@@ -0,0 +1,2 @@
+%YAML 1.1
+--- text
diff --git a/_test/data/spec-05-10.data b/_test/data/spec-05-10.data
new file mode 100644
index 0000000..a4caf91
--- /dev/null
+++ b/_test/data/spec-05-10.data
@@ -0,0 +1,2 @@
+commercial-at: @text
+grave-accent: `text
diff --git a/_test/data/spec-05-10.error b/_test/data/spec-05-10.error
new file mode 100644
index 0000000..46f776e
--- /dev/null
+++ b/_test/data/spec-05-10.error
@@ -0,0 +1,3 @@
+ERROR:
+ Reserved indicators can't
+ start a plain scalar.
diff --git a/_test/data/spec-05-11.canonical b/_test/data/spec-05-11.canonical
new file mode 100644
index 0000000..fc25bef
--- /dev/null
+++ b/_test/data/spec-05-11.canonical
@@ -0,0 +1,6 @@
+%YAML 1.1
+--- !!str
+"Generic line break (no glyph)\n\
+ Generic line break (glyphed)\n\
+ Line separator\u2028\
+ Paragraph separator\u2029"
diff --git a/_test/data/spec-05-11.data b/_test/data/spec-05-11.data
new file mode 100644
index 0000000..b448b75
--- /dev/null
+++ b/_test/data/spec-05-11.data
@@ -0,0 +1,3 @@
+|
+ Generic line break (no glyph)
+ Generic line break (glyphed)… Line separator
 Paragraph separator
 \ No newline at end of file
diff --git a/_test/data/spec-05-12.data b/_test/data/spec-05-12.data
new file mode 100644
index 0000000..7c3ad7f
--- /dev/null
+++ b/_test/data/spec-05-12.data
@@ -0,0 +1,9 @@
+# Tabs do's and don'ts:
+# comment:
+quoted: "Quoted "
+block: |
+ void main() {
+ printf("Hello, world!\n");
+ }
+elsewhere: # separation
+ indentation, in plain scalar
diff --git a/_test/data/spec-05-12.error b/_test/data/spec-05-12.error
new file mode 100644
index 0000000..8aad4c8
--- /dev/null
+++ b/_test/data/spec-05-12.error
@@ -0,0 +1,8 @@
+ERROR:
+ Tabs may appear inside
+ comments and quoted or
+ block scalar content.
+ Tabs must not appear
+ elsewhere, such as
+ in indentation and
+ separation spaces.
diff --git a/_test/data/spec-05-13.canonical b/_test/data/spec-05-13.canonical
new file mode 100644
index 0000000..90c1c5c
--- /dev/null
+++ b/_test/data/spec-05-13.canonical
@@ -0,0 +1,5 @@
+%YAML 1.1
+--- !!str
+"Text containing \
+ both space and \
+ tab characters"
diff --git a/_test/data/spec-05-13.data b/_test/data/spec-05-13.data
new file mode 100644
index 0000000..fce7951
--- /dev/null
+++ b/_test/data/spec-05-13.data
@@ -0,0 +1,3 @@
+ "Text containing
+ both space and
+ tab characters"
diff --git a/_test/data/spec-05-14.canonical b/_test/data/spec-05-14.canonical
new file mode 100644
index 0000000..4bff01c
--- /dev/null
+++ b/_test/data/spec-05-14.canonical
@@ -0,0 +1,7 @@
+%YAML 1.1
+---
+"Fun with \x5C
+ \x22 \x07 \x08 \x1B \x0C
+ \x0A \x0D \x09 \x0B \x00
+ \x20 \xA0 \x85 \u2028 \u2029
+ A A A"
diff --git a/_test/data/spec-05-14.data b/_test/data/spec-05-14.data
new file mode 100644
index 0000000..d6e8ce4
--- /dev/null
+++ b/_test/data/spec-05-14.data
@@ -0,0 +1,2 @@
+"Fun with \\
+ \" \a \b \e \f \… \n \r \t \v \0 \
 \ \_ \N \L \P \
 \x41 \u0041 \U00000041"
diff --git a/_test/data/spec-05-15.data b/_test/data/spec-05-15.data
new file mode 100644
index 0000000..7bf12b6
--- /dev/null
+++ b/_test/data/spec-05-15.data
@@ -0,0 +1,3 @@
+Bad escapes:
+ "\c
+ \xq-"
diff --git a/_test/data/spec-05-15.error b/_test/data/spec-05-15.error
new file mode 100644
index 0000000..71ffbd9
--- /dev/null
+++ b/_test/data/spec-05-15.error
@@ -0,0 +1,3 @@
+ERROR:
+- c is an invalid escaped character.
+- q and - are invalid hex digits.
diff --git a/_test/data/spec-06-01.canonical b/_test/data/spec-06-01.canonical
new file mode 100644
index 0000000..f17ec92
--- /dev/null
+++ b/_test/data/spec-06-01.canonical
@@ -0,0 +1,15 @@
+%YAML 1.1
+---
+!!map {
+ ? !!str "Not indented"
+ : !!map {
+ ? !!str "By one space"
+ : !!str "By four\n spaces\n",
+ ? !!str "Flow style"
+ : !!seq [
+ !!str "By two",
+ !!str "Also by two",
+ !!str "Still by two",
+ ]
+ }
+}
diff --git a/_test/data/spec-06-01.data b/_test/data/spec-06-01.data
new file mode 100644
index 0000000..6134ba1
--- /dev/null
+++ b/_test/data/spec-06-01.data
@@ -0,0 +1,14 @@
+ # Leading comment line spaces are
+ # neither content nor indentation.
+
+Not indented:
+ By one space: |
+ By four
+ spaces
+ Flow style: [ # Leading spaces
+ By two, # in flow style
+ Also by two, # are neither
+# Tabs are not allowed:
+# Still by two # content nor
+ Still by two # content nor
+ ] # indentation.
diff --git a/_test/data/spec-06-02.data b/_test/data/spec-06-02.data
new file mode 100644
index 0000000..ff741e5
--- /dev/null
+++ b/_test/data/spec-06-02.data
@@ -0,0 +1,3 @@
+ # Comment
+
+
diff --git a/_test/data/spec-06-02.empty b/_test/data/spec-06-02.empty
new file mode 100644
index 0000000..bfffa8b
--- /dev/null
+++ b/_test/data/spec-06-02.empty
@@ -0,0 +1,2 @@
+# This stream contains no
+# documents, only comments.
diff --git a/_test/data/spec-06-03.canonical b/_test/data/spec-06-03.canonical
new file mode 100644
index 0000000..ec26902
--- /dev/null
+++ b/_test/data/spec-06-03.canonical
@@ -0,0 +1,6 @@
+%YAML 1.1
+---
+!!map {
+ ? !!str "key"
+ : !!str "value"
+}
diff --git a/_test/data/spec-06-03.data b/_test/data/spec-06-03.data
new file mode 100644
index 0000000..9db0912
--- /dev/null
+++ b/_test/data/spec-06-03.data
@@ -0,0 +1,2 @@
+key: # Comment
+ value
diff --git a/_test/data/spec-06-04.canonical b/_test/data/spec-06-04.canonical
new file mode 100644
index 0000000..ec26902
--- /dev/null
+++ b/_test/data/spec-06-04.canonical
@@ -0,0 +1,6 @@
+%YAML 1.1
+---
+!!map {
+ ? !!str "key"
+ : !!str "value"
+}
diff --git a/_test/data/spec-06-04.data b/_test/data/spec-06-04.data
new file mode 100644
index 0000000..86308dd
--- /dev/null
+++ b/_test/data/spec-06-04.data
@@ -0,0 +1,4 @@
+key: # Comment
+ # lines
+ value
+
diff --git a/_test/data/spec-06-05.canonical b/_test/data/spec-06-05.canonical
new file mode 100644
index 0000000..8da431d
--- /dev/null
+++ b/_test/data/spec-06-05.canonical
@@ -0,0 +1,16 @@
+%YAML 1.1
+---
+!!map {
+ ? !!map {
+ ? !!str "first"
+ : !!str "Sammy",
+ ? !!str "last"
+ : !!str "Sosa"
+ }
+ : !!map {
+ ? !!str "hr"
+ : !!int "65",
+ ? !!str "avg"
+ : !!float "0.278"
+ }
+}
diff --git a/_test/data/spec-06-05.data b/_test/data/spec-06-05.data
new file mode 100644
index 0000000..37613f5
--- /dev/null
+++ b/_test/data/spec-06-05.data
@@ -0,0 +1,6 @@
+{ first: Sammy, last: Sosa }:
+# Statistics:
+ hr: # Home runs
+ 65
+ avg: # Average
+ 0.278
diff --git a/_test/data/spec-06-06.canonical b/_test/data/spec-06-06.canonical
new file mode 100644
index 0000000..513d07a
--- /dev/null
+++ b/_test/data/spec-06-06.canonical
@@ -0,0 +1,10 @@
+%YAML 1.1
+---
+!!map {
+ ? !!str "plain"
+ : !!str "text lines",
+ ? !!str "quoted"
+ : !!str "text lines",
+ ? !!str "block"
+ : !!str "text\n lines\n"
+}
diff --git a/_test/data/spec-06-06.data b/_test/data/spec-06-06.data
new file mode 100644
index 0000000..2f62d08
--- /dev/null
+++ b/_test/data/spec-06-06.data
@@ -0,0 +1,7 @@
+plain: text
+ lines
+quoted: "text
+ lines"
+block: |
+ text
+ lines
diff --git a/_test/data/spec-06-07.canonical b/_test/data/spec-06-07.canonical
new file mode 100644
index 0000000..11357e4
--- /dev/null
+++ b/_test/data/spec-06-07.canonical
@@ -0,0 +1,6 @@
+%YAML 1.1
+---
+!!seq [
+ !!str "foo\nbar",
+ !!str "foo\n\nbar"
+]
diff --git a/_test/data/spec-06-07.data b/_test/data/spec-06-07.data
new file mode 100644
index 0000000..130cfa7
--- /dev/null
+++ b/_test/data/spec-06-07.data
@@ -0,0 +1,8 @@
+- foo
+
+ bar
+- |-
+ foo
+
+ bar
+
diff --git a/_test/data/spec-06-08.canonical b/_test/data/spec-06-08.canonical
new file mode 100644
index 0000000..cc72bc8
--- /dev/null
+++ b/_test/data/spec-06-08.canonical
@@ -0,0 +1,5 @@
+%YAML 1.1
+--- !!str
+"specific\L\
+ trimmed\n\n\n\
+ as space"
diff --git a/_test/data/spec-06-08.data b/_test/data/spec-06-08.data
new file mode 100644
index 0000000..f2896ed
--- /dev/null
+++ b/_test/data/spec-06-08.data
@@ -0,0 +1,2 @@
+>-
+ specific
 trimmed… … …… as… space
diff --git a/_test/data/spec-07-01.canonical b/_test/data/spec-07-01.canonical
new file mode 100644
index 0000000..8c8c48d
--- /dev/null
+++ b/_test/data/spec-07-01.canonical
@@ -0,0 +1,3 @@
+%YAML 1.1
+--- !!str
+"foo"
diff --git a/_test/data/spec-07-01.data b/_test/data/spec-07-01.data
new file mode 100644
index 0000000..2113eb6
--- /dev/null
+++ b/_test/data/spec-07-01.data
@@ -0,0 +1,3 @@
+%FOO bar baz # Should be ignored
+ # with a warning.
+--- "foo"
diff --git a/_test/data/spec-07-01.skip-ext b/_test/data/spec-07-01.skip-ext
new file mode 100644
index 0000000..e69de29
--- /dev/null
+++ b/_test/data/spec-07-01.skip-ext
diff --git a/_test/data/spec-07-02.canonical b/_test/data/spec-07-02.canonical
new file mode 100644
index 0000000..cb7dd1c
--- /dev/null
+++ b/_test/data/spec-07-02.canonical
@@ -0,0 +1,3 @@
+%YAML 1.1
+---
+!!str "foo"
diff --git a/_test/data/spec-07-02.data b/_test/data/spec-07-02.data
new file mode 100644
index 0000000..c8b7322
--- /dev/null
+++ b/_test/data/spec-07-02.data
@@ -0,0 +1,4 @@
+%YAML 1.2 # Attempt parsing
+ # with a warning
+---
+"foo"
diff --git a/_test/data/spec-07-02.skip-ext b/_test/data/spec-07-02.skip-ext
new file mode 100644
index 0000000..e69de29
--- /dev/null
+++ b/_test/data/spec-07-02.skip-ext
diff --git a/_test/data/spec-07-03.data b/_test/data/spec-07-03.data
new file mode 100644
index 0000000..4bfa07a
--- /dev/null
+++ b/_test/data/spec-07-03.data
@@ -0,0 +1,3 @@
+%YAML 1.1
+%YAML 1.1
+foo
diff --git a/_test/data/spec-07-03.error b/_test/data/spec-07-03.error
new file mode 100644
index 0000000..b0ac446
--- /dev/null
+++ b/_test/data/spec-07-03.error
@@ -0,0 +1,3 @@
+ERROR:
+The YAML directive must only be
+given at most once per document.
diff --git a/_test/data/spec-07-04.canonical b/_test/data/spec-07-04.canonical
new file mode 100644
index 0000000..cb7dd1c
--- /dev/null
+++ b/_test/data/spec-07-04.canonical
@@ -0,0 +1,3 @@
+%YAML 1.1
+---
+!!str "foo"
diff --git a/_test/data/spec-07-04.data b/_test/data/spec-07-04.data
new file mode 100644
index 0000000..50f5ab9
--- /dev/null
+++ b/_test/data/spec-07-04.data
@@ -0,0 +1,3 @@
+%TAG !yaml! tag:yaml.org,2002:
+---
+!yaml!str "foo"
diff --git a/_test/data/spec-07-05.data b/_test/data/spec-07-05.data
new file mode 100644
index 0000000..7276eae
--- /dev/null
+++ b/_test/data/spec-07-05.data
@@ -0,0 +1,3 @@
+%TAG ! !foo
+%TAG ! !foo
+bar
diff --git a/_test/data/spec-07-05.error b/_test/data/spec-07-05.error
new file mode 100644
index 0000000..5601b19
--- /dev/null
+++ b/_test/data/spec-07-05.error
@@ -0,0 +1,4 @@
+ERROR:
+The TAG directive must only
+be given at most once per
+handle in the same document.
diff --git a/_test/data/spec-07-06.canonical b/_test/data/spec-07-06.canonical
new file mode 100644
index 0000000..bddf616
--- /dev/null
+++ b/_test/data/spec-07-06.canonical
@@ -0,0 +1,6 @@
+%YAML 1.1
+---
+!!seq [
+ !<!foobar> "baz",
+ !<tag:yaml.org,2002:str> "string"
+]
diff --git a/_test/data/spec-07-06.data b/_test/data/spec-07-06.data
new file mode 100644
index 0000000..d9854cb
--- /dev/null
+++ b/_test/data/spec-07-06.data
@@ -0,0 +1,5 @@
+%TAG ! !foo
+%TAG !yaml! tag:yaml.org,2002:
+---
+- !bar "baz"
+- !yaml!str "string"
diff --git a/_test/data/spec-07-07a.canonical b/_test/data/spec-07-07a.canonical
new file mode 100644
index 0000000..fa086df
--- /dev/null
+++ b/_test/data/spec-07-07a.canonical
@@ -0,0 +1,3 @@
+%YAML 1.1
+---
+!<!foo> "bar"
diff --git a/_test/data/spec-07-07a.data b/_test/data/spec-07-07a.data
new file mode 100644
index 0000000..9d42ec3
--- /dev/null
+++ b/_test/data/spec-07-07a.data
@@ -0,0 +1,2 @@
+# Private application:
+!foo "bar"
diff --git a/_test/data/spec-07-07b.canonical b/_test/data/spec-07-07b.canonical
new file mode 100644
index 0000000..fe917d8
--- /dev/null
+++ b/_test/data/spec-07-07b.canonical
@@ -0,0 +1,3 @@
+%YAML 1.1
+---
+!<tag:ben-kiki.org,2000:app/foo> "bar"
diff --git a/_test/data/spec-07-07b.data b/_test/data/spec-07-07b.data
new file mode 100644
index 0000000..2d36d0e
--- /dev/null
+++ b/_test/data/spec-07-07b.data
@@ -0,0 +1,4 @@
+# Migrated to global:
+%TAG ! tag:ben-kiki.org,2000:app/
+---
+!foo "bar"
diff --git a/_test/data/spec-07-08.canonical b/_test/data/spec-07-08.canonical
new file mode 100644
index 0000000..703aa7b
--- /dev/null
+++ b/_test/data/spec-07-08.canonical
@@ -0,0 +1,7 @@
+%YAML 1.1
+---
+!!seq [
+ !<!foo> "bar",
+ !<tag:yaml.org,2002:str> "string",
+ !<tag:ben-kiki.org,2000:type> "baz"
+]
diff --git a/_test/data/spec-07-08.data b/_test/data/spec-07-08.data
new file mode 100644
index 0000000..e2c6d9e
--- /dev/null
+++ b/_test/data/spec-07-08.data
@@ -0,0 +1,9 @@
+# Explicitly specify default settings:
+%TAG ! !
+%TAG !! tag:yaml.org,2002:
+# Named handles have no default:
+%TAG !o! tag:ben-kiki.org,2000:
+---
+- !foo "bar"
+- !!str "string"
+- !o!type "baz"
diff --git a/_test/data/spec-07-09.canonical b/_test/data/spec-07-09.canonical
new file mode 100644
index 0000000..32d9e94
--- /dev/null
+++ b/_test/data/spec-07-09.canonical
@@ -0,0 +1,9 @@
+%YAML 1.1
+---
+!!str "foo"
+%YAML 1.1
+---
+!!str "bar"
+%YAML 1.1
+---
+!!str "baz"
diff --git a/_test/data/spec-07-09.data b/_test/data/spec-07-09.data
new file mode 100644
index 0000000..1209d47
--- /dev/null
+++ b/_test/data/spec-07-09.data
@@ -0,0 +1,11 @@
+---
+foo
+...
+# Repeated end marker.
+...
+---
+bar
+# No end marker.
+---
+baz
+...
diff --git a/_test/data/spec-07-10.canonical b/_test/data/spec-07-10.canonical
new file mode 100644
index 0000000..1db650a
--- /dev/null
+++ b/_test/data/spec-07-10.canonical
@@ -0,0 +1,15 @@
+%YAML 1.1
+---
+!!str "Root flow scalar"
+%YAML 1.1
+---
+!!str "Root block scalar\n"
+%YAML 1.1
+---
+!!map {
+ ? !!str "foo"
+ : !!str "bar"
+}
+---
+#!!str ""
+!!null ""
diff --git a/_test/data/spec-07-10.data b/_test/data/spec-07-10.data
new file mode 100644
index 0000000..6939b39
--- /dev/null
+++ b/_test/data/spec-07-10.data
@@ -0,0 +1,11 @@
+"Root flow
+ scalar"
+--- !!str >
+ Root block
+ scalar
+---
+# Root collection:
+foo : bar
+... # Is optional.
+---
+# Explicit document may be empty.
diff --git a/_test/data/spec-07-11.data b/_test/data/spec-07-11.data
new file mode 100644
index 0000000..d11302d
--- /dev/null
+++ b/_test/data/spec-07-11.data
@@ -0,0 +1,2 @@
+# A stream may contain
+# no documents.
diff --git a/_test/data/spec-07-11.empty b/_test/data/spec-07-11.empty
new file mode 100644
index 0000000..bfffa8b
--- /dev/null
+++ b/_test/data/spec-07-11.empty
@@ -0,0 +1,2 @@
+# This stream contains no
+# documents, only comments.
diff --git a/_test/data/spec-07-12a.canonical b/_test/data/spec-07-12a.canonical
new file mode 100644
index 0000000..efc116f
--- /dev/null
+++ b/_test/data/spec-07-12a.canonical
@@ -0,0 +1,6 @@
+%YAML 1.1
+---
+!!map {
+ ? !!str "foo"
+ : !!str "bar"
+}
diff --git a/_test/data/spec-07-12a.data b/_test/data/spec-07-12a.data
new file mode 100644
index 0000000..3807d57
--- /dev/null
+++ b/_test/data/spec-07-12a.data
@@ -0,0 +1,3 @@
+# Implicit document. Root
+# collection (mapping) node.
+foo : bar
diff --git a/_test/data/spec-07-12b.canonical b/_test/data/spec-07-12b.canonical
new file mode 100644
index 0000000..04bcffc
--- /dev/null
+++ b/_test/data/spec-07-12b.canonical
@@ -0,0 +1,3 @@
+%YAML 1.1
+---
+!!str "Text content\n"
diff --git a/_test/data/spec-07-12b.data b/_test/data/spec-07-12b.data
new file mode 100644
index 0000000..43250db
--- /dev/null
+++ b/_test/data/spec-07-12b.data
@@ -0,0 +1,4 @@
+# Explicit document. Root
+# scalar (literal) node.
+--- |
+ Text content
diff --git a/_test/data/spec-07-13.canonical b/_test/data/spec-07-13.canonical
new file mode 100644
index 0000000..5af71e9
--- /dev/null
+++ b/_test/data/spec-07-13.canonical
@@ -0,0 +1,9 @@
+%YAML 1.1
+---
+!!str "First document"
+---
+!<!foo> "No directives"
+---
+!<!foobar> "With directives"
+---
+!<!baz> "Reset settings"
diff --git a/_test/data/spec-07-13.data b/_test/data/spec-07-13.data
new file mode 100644
index 0000000..ba7ec63
--- /dev/null
+++ b/_test/data/spec-07-13.data
@@ -0,0 +1,9 @@
+! "First document"
+---
+!foo "No directives"
+%TAG ! !foo
+---
+!bar "With directives"
+%YAML 1.1
+---
+!baz "Reset settings"
diff --git a/_test/data/spec-08-01.canonical b/_test/data/spec-08-01.canonical
new file mode 100644
index 0000000..69e4161
--- /dev/null
+++ b/_test/data/spec-08-01.canonical
@@ -0,0 +1,8 @@
+%YAML 1.1
+---
+!!map {
+ ? &A1 !!str "foo"
+ : !!str "bar",
+ ? &A2 !!str "baz"
+ : *A1
+}
diff --git a/_test/data/spec-08-01.data b/_test/data/spec-08-01.data
new file mode 100644
index 0000000..48986ec
--- /dev/null
+++ b/_test/data/spec-08-01.data
@@ -0,0 +1,2 @@
+!!str &a1 "foo" : !!str bar
+&a2 baz : *a1
diff --git a/_test/data/spec-08-02.canonical b/_test/data/spec-08-02.canonical
new file mode 100644
index 0000000..dd6f76e
--- /dev/null
+++ b/_test/data/spec-08-02.canonical
@@ -0,0 +1,8 @@
+%YAML 1.1
+---
+!!map {
+ ? !!str "First occurrence"
+ : &A !!str "Value",
+ ? !!str "Second occurrence"
+ : *A
+}
diff --git a/_test/data/spec-08-02.data b/_test/data/spec-08-02.data
new file mode 100644
index 0000000..600d179
--- /dev/null
+++ b/_test/data/spec-08-02.data
@@ -0,0 +1,2 @@
+First occurrence: &anchor Value
+Second occurrence: *anchor
diff --git a/_test/data/spec-08-03.canonical b/_test/data/spec-08-03.canonical
new file mode 100644
index 0000000..be7ea8f
--- /dev/null
+++ b/_test/data/spec-08-03.canonical
@@ -0,0 +1,6 @@
+%YAML 1.1
+---
+!!map {
+ ? !<tag:yaml.org,2002:str> "foo"
+ : !<!bar> "baz"
+}
diff --git a/_test/data/spec-08-03.data b/_test/data/spec-08-03.data
new file mode 100644
index 0000000..8e51f52
--- /dev/null
+++ b/_test/data/spec-08-03.data
@@ -0,0 +1,2 @@
+!<tag:yaml.org,2002:str> foo :
+ !<!bar> baz
diff --git a/_test/data/spec-08-04.data b/_test/data/spec-08-04.data
new file mode 100644
index 0000000..f7d1b01
--- /dev/null
+++ b/_test/data/spec-08-04.data
@@ -0,0 +1,2 @@
+- !<!> foo
+- !<$:?> bar
diff --git a/_test/data/spec-08-04.error b/_test/data/spec-08-04.error
new file mode 100644
index 0000000..6066375
--- /dev/null
+++ b/_test/data/spec-08-04.error
@@ -0,0 +1,6 @@
+ERROR:
+- Verbatim tags aren't resolved,
+ so ! is invalid.
+- The $:? tag is neither a global
+ URI tag nor a local tag starting
+ with “!”.
diff --git a/_test/data/spec-08-05.canonical b/_test/data/spec-08-05.canonical
new file mode 100644
index 0000000..a5c710a
--- /dev/null
+++ b/_test/data/spec-08-05.canonical
@@ -0,0 +1,7 @@
+%YAML 1.1
+---
+!!seq [
+ !<!local> "foo",
+ !<tag:yaml.org,2002:str> "bar",
+ !<tag:ben-kiki.org,2000:type> "baz",
+]
diff --git a/_test/data/spec-08-05.data b/_test/data/spec-08-05.data
new file mode 100644
index 0000000..93576ed
--- /dev/null
+++ b/_test/data/spec-08-05.data
@@ -0,0 +1,5 @@
+%TAG !o! tag:ben-kiki.org,2000:
+---
+- !local foo
+- !!str bar
+- !o!type baz
diff --git a/_test/data/spec-08-06.data b/_test/data/spec-08-06.data
new file mode 100644
index 0000000..8580010
--- /dev/null
+++ b/_test/data/spec-08-06.data
@@ -0,0 +1,5 @@
+%TAG !o! tag:ben-kiki.org,2000:
+---
+- !$a!b foo
+- !o! bar
+- !h!type baz
diff --git a/_test/data/spec-08-06.error b/_test/data/spec-08-06.error
new file mode 100644
index 0000000..fb76f42
--- /dev/null
+++ b/_test/data/spec-08-06.error
@@ -0,0 +1,4 @@
+ERROR:
+- The !$a! looks like a handle.
+- The !o! handle has no suffix.
+- The !h! handle wasn't declared.
diff --git a/_test/data/spec-08-07.canonical b/_test/data/spec-08-07.canonical
new file mode 100644
index 0000000..e2f43d9
--- /dev/null
+++ b/_test/data/spec-08-07.canonical
@@ -0,0 +1,8 @@
+%YAML 1.1
+---
+!!seq [
+ !<tag:yaml.org,2002:str> "12",
+ !<tag:yaml.org,2002:int> "12",
+# !<tag:yaml.org,2002:str> "12",
+ !<tag:yaml.org,2002:int> "12",
+]
diff --git a/_test/data/spec-08-07.data b/_test/data/spec-08-07.data
new file mode 100644
index 0000000..98aa565
--- /dev/null
+++ b/_test/data/spec-08-07.data
@@ -0,0 +1,4 @@
+# Assuming conventional resolution:
+- "12"
+- 12
+- ! 12
diff --git a/_test/data/spec-08-08.canonical b/_test/data/spec-08-08.canonical
new file mode 100644
index 0000000..d3f8b1a
--- /dev/null
+++ b/_test/data/spec-08-08.canonical
@@ -0,0 +1,15 @@
+%YAML 1.1
+---
+!!map {
+ ? !!str "foo"
+ : !!str "bar baz"
+}
+%YAML 1.1
+---
+!!str "foo bar"
+%YAML 1.1
+---
+!!str "foo bar"
+%YAML 1.1
+---
+!!str "foo\n"
diff --git a/_test/data/spec-08-08.data b/_test/data/spec-08-08.data
new file mode 100644
index 0000000..757a93d
--- /dev/null
+++ b/_test/data/spec-08-08.data
@@ -0,0 +1,13 @@
+---
+foo:
+ "bar
+ baz"
+---
+"foo
+ bar"
+---
+foo
+ bar
+--- |
+ foo
+...
diff --git a/_test/data/spec-08-09.canonical b/_test/data/spec-08-09.canonical
new file mode 100644
index 0000000..3805daf
--- /dev/null
+++ b/_test/data/spec-08-09.canonical
@@ -0,0 +1,21 @@
+%YAML 1.1
+--- !!map {
+ ? !!str "scalars" : !!map {
+ ? !!str "plain"
+ : !!str "some text",
+ ? !!str "quoted"
+ : !!map {
+ ? !!str "single"
+ : !!str "some text",
+ ? !!str "double"
+ : !!str "some text"
+ } },
+ ? !!str "collections" : !!map {
+ ? !!str "sequence" : !!seq [
+ !!str "entry",
+ !!map {
+ ? !!str "key" : !!str "value"
+ } ],
+ ? !!str "mapping" : !!map {
+ ? !!str "key" : !!str "value"
+} } }
diff --git a/_test/data/spec-08-09.data b/_test/data/spec-08-09.data
new file mode 100644
index 0000000..69da042
--- /dev/null
+++ b/_test/data/spec-08-09.data
@@ -0,0 +1,11 @@
+---
+scalars:
+ plain: !!str some text
+ quoted:
+ single: 'some text'
+ double: "some text"
+collections:
+ sequence: !!seq [ !!str entry,
+ # Mapping entry:
+ key: value ]
+ mapping: { key: value }
diff --git a/_test/data/spec-08-10.canonical b/_test/data/spec-08-10.canonical
new file mode 100644
index 0000000..8281c5e
--- /dev/null
+++ b/_test/data/spec-08-10.canonical
@@ -0,0 +1,23 @@
+%YAML 1.1
+---
+!!map {
+ ? !!str "block styles" : !!map {
+ ? !!str "scalars" : !!map {
+ ? !!str "literal"
+ : !!str "#!/usr/bin/perl\n\
+ print \"Hello,
+ world!\\n\";\n",
+ ? !!str "folded"
+ : !!str "This sentence
+ is false.\n"
+ },
+ ? !!str "collections" : !!map {
+ ? !!str "sequence" : !!seq [
+ !!str "entry",
+ !!map {
+ ? !!str "key" : !!str "value"
+ }
+ ],
+ ? !!str "mapping" : !!map {
+ ? !!str "key" : !!str "value"
+} } } }
diff --git a/_test/data/spec-08-10.data b/_test/data/spec-08-10.data
new file mode 100644
index 0000000..72acc56
--- /dev/null
+++ b/_test/data/spec-08-10.data
@@ -0,0 +1,15 @@
+block styles:
+ scalars:
+ literal: !!str |
+ #!/usr/bin/perl
+ print "Hello, world!\n";
+ folded: >
+ This sentence
+ is false.
+ collections: !!map
+ sequence: !!seq # Entry:
+ - entry # Plain
+ # Mapping entry:
+ - key: value
+ mapping:
+ key: value
diff --git a/_test/data/spec-08-11.canonical b/_test/data/spec-08-11.canonical
new file mode 100644
index 0000000..dd6f76e
--- /dev/null
+++ b/_test/data/spec-08-11.canonical
@@ -0,0 +1,8 @@
+%YAML 1.1
+---
+!!map {
+ ? !!str "First occurrence"
+ : &A !!str "Value",
+ ? !!str "Second occurrence"
+ : *A
+}
diff --git a/_test/data/spec-08-11.data b/_test/data/spec-08-11.data
new file mode 100644
index 0000000..600d179
--- /dev/null
+++ b/_test/data/spec-08-11.data
@@ -0,0 +1,2 @@
+First occurrence: &anchor Value
+Second occurrence: *anchor
diff --git a/_test/data/spec-08-12.canonical b/_test/data/spec-08-12.canonical
new file mode 100644
index 0000000..93899f4
--- /dev/null
+++ b/_test/data/spec-08-12.canonical
@@ -0,0 +1,10 @@
+%YAML 1.1
+---
+!!seq [
+ !!str "Without properties",
+ &A !!str "Anchored",
+ !!str "Tagged",
+ *A,
+ !!str "",
+ !!str "",
+]
diff --git a/_test/data/spec-08-12.data b/_test/data/spec-08-12.data
new file mode 100644
index 0000000..3d4c6b7
--- /dev/null
+++ b/_test/data/spec-08-12.data
@@ -0,0 +1,8 @@
+[
+ Without properties,
+ &anchor "Anchored",
+ !!str 'Tagged',
+ *anchor, # Alias node
+ !!str , # Empty plain scalar
+ '', # Empty plain scalar
+]
diff --git a/_test/data/spec-08-13.canonical b/_test/data/spec-08-13.canonical
new file mode 100644
index 0000000..618bb7b
--- /dev/null
+++ b/_test/data/spec-08-13.canonical
@@ -0,0 +1,10 @@
+%YAML 1.1
+---
+!!map {
+ ? !!str "foo"
+# : !!str "",
+# ? !!str ""
+ : !!null "",
+ ? !!null ""
+ : !!str "bar",
+}
diff --git a/_test/data/spec-08-13.data b/_test/data/spec-08-13.data
new file mode 100644
index 0000000..ebe663a
--- /dev/null
+++ b/_test/data/spec-08-13.data
@@ -0,0 +1,4 @@
+{
+ ? foo :,
+ ? : bar,
+}
diff --git a/_test/data/spec-08-13.skip-ext b/_test/data/spec-08-13.skip-ext
new file mode 100644
index 0000000..e69de29
--- /dev/null
+++ b/_test/data/spec-08-13.skip-ext
diff --git a/_test/data/spec-08-14.canonical b/_test/data/spec-08-14.canonical
new file mode 100644
index 0000000..11db439
--- /dev/null
+++ b/_test/data/spec-08-14.canonical
@@ -0,0 +1,10 @@
+%YAML 1.1
+---
+!!seq [
+ !!str "flow in block",
+ !!str "Block scalar\n",
+ !!map {
+ ? !!str "foo"
+ : !!str "bar"
+ }
+]
diff --git a/_test/data/spec-08-14.data b/_test/data/spec-08-14.data
new file mode 100644
index 0000000..2fbb1f7
--- /dev/null
+++ b/_test/data/spec-08-14.data
@@ -0,0 +1,5 @@
+- "flow in block"
+- >
+ Block scalar
+- !!map # Block collection
+ foo : bar
diff --git a/_test/data/spec-08-15.canonical b/_test/data/spec-08-15.canonical
new file mode 100644
index 0000000..76f028e
--- /dev/null
+++ b/_test/data/spec-08-15.canonical
@@ -0,0 +1,11 @@
+%YAML 1.1
+---
+!!seq [
+ !!null "",
+ !!map {
+ ? !!str "foo"
+ : !!null "",
+ ? !!null ""
+ : !!str "bar",
+ }
+]
diff --git a/_test/data/spec-08-15.data b/_test/data/spec-08-15.data
new file mode 100644
index 0000000..7c86bcf
--- /dev/null
+++ b/_test/data/spec-08-15.data
@@ -0,0 +1,5 @@
+- # Empty plain scalar
+- ? foo
+ :
+ ?
+ : bar
diff --git a/_test/data/spec-09-01.canonical b/_test/data/spec-09-01.canonical
new file mode 100644
index 0000000..e71a548
--- /dev/null
+++ b/_test/data/spec-09-01.canonical
@@ -0,0 +1,11 @@
+%YAML 1.1
+---
+!!map {
+ ? !!str "simple key"
+ : !!map {
+ ? !!str "also simple"
+ : !!str "value",
+ ? !!str "not a simple key"
+ : !!str "any value"
+ }
+}
diff --git a/_test/data/spec-09-01.data b/_test/data/spec-09-01.data
new file mode 100644
index 0000000..9e83eaf
--- /dev/null
+++ b/_test/data/spec-09-01.data
@@ -0,0 +1,6 @@
+"simple key" : {
+ "also simple" : value,
+ ? "not a
+ simple key" : "any
+ value"
+}
diff --git a/_test/data/spec-09-02.canonical b/_test/data/spec-09-02.canonical
new file mode 100644
index 0000000..6f8f41a
--- /dev/null
+++ b/_test/data/spec-09-02.canonical
@@ -0,0 +1,7 @@
+%YAML 1.1
+---
+!!str "as space \
+ trimmed\n\
+ specific\L\n\
+ escaped\t\n\
+ none"
diff --git a/_test/data/spec-09-02.data b/_test/data/spec-09-02.data
new file mode 100644
index 0000000..d84883d
--- /dev/null
+++ b/_test/data/spec-09-02.data
@@ -0,0 +1,6 @@
+ "as space
+ trimmed
+
+ specific

+ escaped \

+ none"
diff --git a/_test/data/spec-09-03.canonical b/_test/data/spec-09-03.canonical
new file mode 100644
index 0000000..658c6df
--- /dev/null
+++ b/_test/data/spec-09-03.canonical
@@ -0,0 +1,7 @@
+%YAML 1.1
+---
+!!seq [
+ !!str " last",
+ !!str " last",
+ !!str " \tfirst last",
+]
diff --git a/_test/data/spec-09-03.data b/_test/data/spec-09-03.data
new file mode 100644
index 0000000..e0b914d
--- /dev/null
+++ b/_test/data/spec-09-03.data
@@ -0,0 +1,6 @@
+- "
+ last"
+- "
+ last"
+- " first
+ last"
diff --git a/_test/data/spec-09-04.canonical b/_test/data/spec-09-04.canonical
new file mode 100644
index 0000000..fa46632
--- /dev/null
+++ b/_test/data/spec-09-04.canonical
@@ -0,0 +1,6 @@
+%YAML 1.1
+---
+!!str "first \
+ inner 1 \
+ inner 2 \
+ last"
diff --git a/_test/data/spec-09-04.data b/_test/data/spec-09-04.data
new file mode 100644
index 0000000..313a91b
--- /dev/null
+++ b/_test/data/spec-09-04.data
@@ -0,0 +1,4 @@
+ "first
+ inner 1
+ \ inner 2 \
+ last"
diff --git a/_test/data/spec-09-05.canonical b/_test/data/spec-09-05.canonical
new file mode 100644
index 0000000..24d1052
--- /dev/null
+++ b/_test/data/spec-09-05.canonical
@@ -0,0 +1,7 @@
+%YAML 1.1
+---
+!!seq [
+ !!str "first ",
+ !!str "first\nlast",
+ !!str "first inner \tlast",
+]
diff --git a/_test/data/spec-09-05.data b/_test/data/spec-09-05.data
new file mode 100644
index 0000000..624c30e
--- /dev/null
+++ b/_test/data/spec-09-05.data
@@ -0,0 +1,8 @@
+- "first
+ "
+- "first
+
+ last"
+- "first
+ inner
+ \ last"
diff --git a/_test/data/spec-09-06.canonical b/_test/data/spec-09-06.canonical
new file mode 100644
index 0000000..5028772
--- /dev/null
+++ b/_test/data/spec-09-06.canonical
@@ -0,0 +1,3 @@
+%YAML 1.1
+---
+!!str "here's to \"quotes\""
diff --git a/_test/data/spec-09-06.data b/_test/data/spec-09-06.data
new file mode 100644
index 0000000..b038078
--- /dev/null
+++ b/_test/data/spec-09-06.data
@@ -0,0 +1 @@
+ 'here''s to "quotes"'
diff --git a/_test/data/spec-09-07.canonical b/_test/data/spec-09-07.canonical
new file mode 100644
index 0000000..e71a548
--- /dev/null
+++ b/_test/data/spec-09-07.canonical
@@ -0,0 +1,11 @@
+%YAML 1.1
+---
+!!map {
+ ? !!str "simple key"
+ : !!map {
+ ? !!str "also simple"
+ : !!str "value",
+ ? !!str "not a simple key"
+ : !!str "any value"
+ }
+}
diff --git a/_test/data/spec-09-07.data b/_test/data/spec-09-07.data
new file mode 100644
index 0000000..755b54a
--- /dev/null
+++ b/_test/data/spec-09-07.data
@@ -0,0 +1,6 @@
+'simple key' : {
+ 'also simple' : value,
+ ? 'not a
+ simple key' : 'any
+ value'
+}
diff --git a/_test/data/spec-09-08.canonical b/_test/data/spec-09-08.canonical
new file mode 100644
index 0000000..06abdb5
--- /dev/null
+++ b/_test/data/spec-09-08.canonical
@@ -0,0 +1,6 @@
+%YAML 1.1
+---
+!!str "as space \
+ trimmed\n\
+ specific\L\n\
+ none"
diff --git a/_test/data/spec-09-08.data b/_test/data/spec-09-08.data
new file mode 100644
index 0000000..aa4d458
--- /dev/null
+++ b/_test/data/spec-09-08.data
@@ -0,0 +1 @@
+ 'as space … trimmed …… specific
… none'
diff --git a/_test/data/spec-09-09.canonical b/_test/data/spec-09-09.canonical
new file mode 100644
index 0000000..658c6df
--- /dev/null
+++ b/_test/data/spec-09-09.canonical
@@ -0,0 +1,7 @@
+%YAML 1.1
+---
+!!seq [
+ !!str " last",
+ !!str " last",
+ !!str " \tfirst last",
+]
diff --git a/_test/data/spec-09-09.data b/_test/data/spec-09-09.data
new file mode 100644
index 0000000..52171df
--- /dev/null
+++ b/_test/data/spec-09-09.data
@@ -0,0 +1,6 @@
+- '
+ last'
+- '
+ last'
+- ' first
+ last'
diff --git a/_test/data/spec-09-10.canonical b/_test/data/spec-09-10.canonical
new file mode 100644
index 0000000..2028d04
--- /dev/null
+++ b/_test/data/spec-09-10.canonical
@@ -0,0 +1,5 @@
+%YAML 1.1
+---
+!!str "first \
+ inner \
+ last"
diff --git a/_test/data/spec-09-10.data b/_test/data/spec-09-10.data
new file mode 100644
index 0000000..0e41449
--- /dev/null
+++ b/_test/data/spec-09-10.data
@@ -0,0 +1,3 @@
+ 'first
+ inner
+ last'
diff --git a/_test/data/spec-09-11.canonical b/_test/data/spec-09-11.canonical
new file mode 100644
index 0000000..4eb222c
--- /dev/null
+++ b/_test/data/spec-09-11.canonical
@@ -0,0 +1,6 @@
+%YAML 1.1
+---
+!!seq [
+ !!str "first ",
+ !!str "first\nlast",
+]
diff --git a/_test/data/spec-09-11.data b/_test/data/spec-09-11.data
new file mode 100644
index 0000000..5efa873
--- /dev/null
+++ b/_test/data/spec-09-11.data
@@ -0,0 +1,5 @@
+- 'first
+ '
+- 'first
+
+ last'
diff --git a/_test/data/spec-09-12.canonical b/_test/data/spec-09-12.canonical
new file mode 100644
index 0000000..d8e6dce
--- /dev/null
+++ b/_test/data/spec-09-12.canonical
@@ -0,0 +1,12 @@
+%YAML 1.1
+---
+!!seq [
+ !!str "::std::vector",
+ !!str "Up, up, and away!",
+ !!int "-123",
+ !!seq [
+ !!str "::std::vector",
+ !!str "Up, up, and away!",
+ !!int "-123",
+ ]
+]
diff --git a/_test/data/spec-09-12.data b/_test/data/spec-09-12.data
new file mode 100644
index 0000000..b9a3ac5
--- /dev/null
+++ b/_test/data/spec-09-12.data
@@ -0,0 +1,8 @@
+# Outside flow collection:
+- ::std::vector
+- Up, up, and away!
+- -123
+# Inside flow collection:
+- [ '::std::vector',
+ "Up, up, and away!",
+ -123 ]
diff --git a/_test/data/spec-09-13.canonical b/_test/data/spec-09-13.canonical
new file mode 100644
index 0000000..e71a548
--- /dev/null
+++ b/_test/data/spec-09-13.canonical
@@ -0,0 +1,11 @@
+%YAML 1.1
+---
+!!map {
+ ? !!str "simple key"
+ : !!map {
+ ? !!str "also simple"
+ : !!str "value",
+ ? !!str "not a simple key"
+ : !!str "any value"
+ }
+}
diff --git a/_test/data/spec-09-13.data b/_test/data/spec-09-13.data
new file mode 100644
index 0000000..b156386
--- /dev/null
+++ b/_test/data/spec-09-13.data
@@ -0,0 +1,6 @@
+simple key : {
+ also simple : value,
+ ? not a
+ simple key : any
+ value
+}
diff --git a/_test/data/spec-09-14.data b/_test/data/spec-09-14.data
new file mode 100644
index 0000000..97f2316
--- /dev/null
+++ b/_test/data/spec-09-14.data
@@ -0,0 +1,14 @@
+---
+--- ||| : foo
+... >>>: bar
+---
+[
+---
+,
+... ,
+{
+--- :
+... # Nested
+}
+]
+...
diff --git a/_test/data/spec-09-14.error b/_test/data/spec-09-14.error
new file mode 100644
index 0000000..9f3db7b
--- /dev/null
+++ b/_test/data/spec-09-14.error
@@ -0,0 +1,6 @@
+ERROR:
+ The --- and ... document
+ start and end markers must
+ not be specified as the
+ first content line of a
+ non-indented plain scalar.
diff --git a/_test/data/spec-09-15.canonical b/_test/data/spec-09-15.canonical
new file mode 100644
index 0000000..df02040
--- /dev/null
+++ b/_test/data/spec-09-15.canonical
@@ -0,0 +1,18 @@
+%YAML 1.1
+---
+!!map {
+ ? !!str "---"
+ : !!str "foo",
+ ? !!str "..."
+ : !!str "bar"
+}
+%YAML 1.1
+---
+!!seq [
+ !!str "---",
+ !!str "...",
+ !!map {
+ ? !!str "---"
+ : !!str "..."
+ }
+]
diff --git a/_test/data/spec-09-15.data b/_test/data/spec-09-15.data
new file mode 100644
index 0000000..e6863b0
--- /dev/null
+++ b/_test/data/spec-09-15.data
@@ -0,0 +1,13 @@
+---
+"---" : foo
+...: bar
+---
+[
+---,
+...,
+{
+? ---
+: ...
+}
+]
+...
diff --git a/_test/data/spec-09-16.canonical b/_test/data/spec-09-16.canonical
new file mode 100644
index 0000000..06abdb5
--- /dev/null
+++ b/_test/data/spec-09-16.canonical
@@ -0,0 +1,6 @@
+%YAML 1.1
+---
+!!str "as space \
+ trimmed\n\
+ specific\L\n\
+ none"
diff --git a/_test/data/spec-09-16.data b/_test/data/spec-09-16.data
new file mode 100644
index 0000000..473beb9
--- /dev/null
+++ b/_test/data/spec-09-16.data
@@ -0,0 +1,3 @@
+# Tabs are confusing:
+# as space/trimmed/specific/none
+ as space … trimmed …… specific
… none
diff --git a/_test/data/spec-09-17.canonical b/_test/data/spec-09-17.canonical
new file mode 100644
index 0000000..68cb70d
--- /dev/null
+++ b/_test/data/spec-09-17.canonical
@@ -0,0 +1,4 @@
+%YAML 1.1
+---
+!!str "first line\n\
+ more line"
diff --git a/_test/data/spec-09-17.data b/_test/data/spec-09-17.data
new file mode 100644
index 0000000..97bc46c
--- /dev/null
+++ b/_test/data/spec-09-17.data
@@ -0,0 +1,3 @@
+ first line
+
+ more line
diff --git a/_test/data/spec-09-18.canonical b/_test/data/spec-09-18.canonical
new file mode 100644
index 0000000..f21428f
--- /dev/null
+++ b/_test/data/spec-09-18.canonical
@@ -0,0 +1,8 @@
+%YAML 1.1
+---
+!!seq [
+ !!str "literal\n",
+ !!str " folded\n",
+ !!str "keep\n\n",
+ !!str " strip",
+]
diff --git a/_test/data/spec-09-18.data b/_test/data/spec-09-18.data
new file mode 100644
index 0000000..68c5d7c
--- /dev/null
+++ b/_test/data/spec-09-18.data
@@ -0,0 +1,9 @@
+- | # Just the style
+ literal
+- >1 # Indentation indicator
+ folded
+- |+ # Chomping indicator
+ keep
+
+- >-1 # Both indicators
+ strip
diff --git a/_test/data/spec-09-19.canonical b/_test/data/spec-09-19.canonical
new file mode 100644
index 0000000..3e828d7
--- /dev/null
+++ b/_test/data/spec-09-19.canonical
@@ -0,0 +1,6 @@
+%YAML 1.1
+---
+!!seq [
+ !!str "literal\n",
+ !!str "folded\n",
+]
diff --git a/_test/data/spec-09-19.data b/_test/data/spec-09-19.data
new file mode 100644
index 0000000..f0e589d
--- /dev/null
+++ b/_test/data/spec-09-19.data
@@ -0,0 +1,4 @@
+- |
+ literal
+- >
+ folded
diff --git a/_test/data/spec-09-20.canonical b/_test/data/spec-09-20.canonical
new file mode 100644
index 0000000..d03bef5
--- /dev/null
+++ b/_test/data/spec-09-20.canonical
@@ -0,0 +1,8 @@
+%YAML 1.1
+---
+!!seq [
+ !!str "detected\n",
+ !!str "\n\n# detected\n",
+ !!str " explicit\n",
+ !!str "\t\ndetected\n",
+]
diff --git a/_test/data/spec-09-20.data b/_test/data/spec-09-20.data
new file mode 100644
index 0000000..39bee04
--- /dev/null
+++ b/_test/data/spec-09-20.data
@@ -0,0 +1,11 @@
+- |
+ detected
+- >
+
+
+ # detected
+- |1
+ explicit
+- >
+
+ detected
diff --git a/_test/data/spec-09-20.skip-ext b/_test/data/spec-09-20.skip-ext
new file mode 100644
index 0000000..e69de29
--- /dev/null
+++ b/_test/data/spec-09-20.skip-ext
diff --git a/_test/data/spec-09-21.data b/_test/data/spec-09-21.data
new file mode 100644
index 0000000..0fdd14f
--- /dev/null
+++ b/_test/data/spec-09-21.data
@@ -0,0 +1,8 @@
+- |
+
+ text
+- >
+ text
+ text
+- |1
+ text
diff --git a/_test/data/spec-09-21.error b/_test/data/spec-09-21.error
new file mode 100644
index 0000000..1379ca5
--- /dev/null
+++ b/_test/data/spec-09-21.error
@@ -0,0 +1,7 @@
+ERROR:
+- A leading all-space line must
+ not have too many spaces.
+- A following text line must
+ not be less indented.
+- The text is less indented
+ than the indicated level.
diff --git a/_test/data/spec-09-22.canonical b/_test/data/spec-09-22.canonical
new file mode 100644
index 0000000..c1bbcd2
--- /dev/null
+++ b/_test/data/spec-09-22.canonical
@@ -0,0 +1,10 @@
+%YAML 1.1
+---
+!!map {
+ ? !!str "strip"
+ : !!str "text",
+ ? !!str "clip"
+ : !!str "text\n",
+ ? !!str "keep"
+ : !!str "text\L",
+}
diff --git a/_test/data/spec-09-22.data b/_test/data/spec-09-22.data
new file mode 100644
index 0000000..0dd51eb
--- /dev/null
+++ b/_test/data/spec-09-22.data
@@ -0,0 +1,4 @@
+strip: |-
+ text
clip: |
+ text…keep: |+
+ text
 \ No newline at end of file
diff --git a/_test/data/spec-09-23.canonical b/_test/data/spec-09-23.canonical
new file mode 100644
index 0000000..c4444ca
--- /dev/null
+++ b/_test/data/spec-09-23.canonical
@@ -0,0 +1,10 @@
+%YAML 1.1
+---
+!!map {
+ ? !!str "strip"
+ : !!str "# text",
+ ? !!str "clip"
+ : !!str "# text\n",
+ ? !!str "keep"
+ : !!str "# text\L\n",
+}
diff --git a/_test/data/spec-09-23.data b/_test/data/spec-09-23.data
new file mode 100644
index 0000000..8972d2b
--- /dev/null
+++ b/_test/data/spec-09-23.data
@@ -0,0 +1,11 @@
+ # Strip
+ # Comments:
+strip: |-
+ # text
 
 # Clip
+ # comments:
+…clip: |
+ # text… 
 # Keep
+ # comments:
+…keep: |+
+ # text
… # Trail
+ # comments.
diff --git a/_test/data/spec-09-24.canonical b/_test/data/spec-09-24.canonical
new file mode 100644
index 0000000..45a99b0
--- /dev/null
+++ b/_test/data/spec-09-24.canonical
@@ -0,0 +1,10 @@
+%YAML 1.1
+---
+!!map {
+ ? !!str "strip"
+ : !!str "",
+ ? !!str "clip"
+ : !!str "",
+ ? !!str "keep"
+ : !!str "\n",
+}
diff --git a/_test/data/spec-09-24.data b/_test/data/spec-09-24.data
new file mode 100644
index 0000000..de0b64b
--- /dev/null
+++ b/_test/data/spec-09-24.data
@@ -0,0 +1,6 @@
+strip: >-
+
+clip: >
+
+keep: |+
+
diff --git a/_test/data/spec-09-25.canonical b/_test/data/spec-09-25.canonical
new file mode 100644
index 0000000..9d2327b
--- /dev/null
+++ b/_test/data/spec-09-25.canonical
@@ -0,0 +1,4 @@
+%YAML 1.1
+---
+!!str "literal\n\
+ \ttext\n"
diff --git a/_test/data/spec-09-25.data b/_test/data/spec-09-25.data
new file mode 100644
index 0000000..f6303a1
--- /dev/null
+++ b/_test/data/spec-09-25.data
@@ -0,0 +1,3 @@
+| # Simple block scalar
+ literal
+ text
diff --git a/_test/data/spec-09-26.canonical b/_test/data/spec-09-26.canonical
new file mode 100644
index 0000000..3029a11
--- /dev/null
+++ b/_test/data/spec-09-26.canonical
@@ -0,0 +1,3 @@
+%YAML 1.1
+---
+!!str "\n\nliteral\n\ntext\n"
diff --git a/_test/data/spec-09-26.data b/_test/data/spec-09-26.data
new file mode 100644
index 0000000..f28555a
--- /dev/null
+++ b/_test/data/spec-09-26.data
@@ -0,0 +1,8 @@
+|
+
+
+ literal
+
+ text
+
+ # Comment
diff --git a/_test/data/spec-09-27.canonical b/_test/data/spec-09-27.canonical
new file mode 100644
index 0000000..3029a11
--- /dev/null
+++ b/_test/data/spec-09-27.canonical
@@ -0,0 +1,3 @@
+%YAML 1.1
+---
+!!str "\n\nliteral\n\ntext\n"
diff --git a/_test/data/spec-09-27.data b/_test/data/spec-09-27.data
new file mode 100644
index 0000000..f28555a
--- /dev/null
+++ b/_test/data/spec-09-27.data
@@ -0,0 +1,8 @@
+|
+
+
+ literal
+
+ text
+
+ # Comment
diff --git a/_test/data/spec-09-28.canonical b/_test/data/spec-09-28.canonical
new file mode 100644
index 0000000..3029a11
--- /dev/null
+++ b/_test/data/spec-09-28.canonical
@@ -0,0 +1,3 @@
+%YAML 1.1
+---
+!!str "\n\nliteral\n\ntext\n"
diff --git a/_test/data/spec-09-28.data b/_test/data/spec-09-28.data
new file mode 100644
index 0000000..f28555a
--- /dev/null
+++ b/_test/data/spec-09-28.data
@@ -0,0 +1,8 @@
+|
+
+
+ literal
+
+ text
+
+ # Comment
diff --git a/_test/data/spec-09-29.canonical b/_test/data/spec-09-29.canonical
new file mode 100644
index 0000000..0980789
--- /dev/null
+++ b/_test/data/spec-09-29.canonical
@@ -0,0 +1,4 @@
+%YAML 1.1
+---
+!!str "folded text\n\
+ \tlines\n"
diff --git a/_test/data/spec-09-29.data b/_test/data/spec-09-29.data
new file mode 100644
index 0000000..82e611f
--- /dev/null
+++ b/_test/data/spec-09-29.data
@@ -0,0 +1,4 @@
+> # Simple folded scalar
+ folded
+ text
+ lines
diff --git a/_test/data/spec-09-30.canonical b/_test/data/spec-09-30.canonical
new file mode 100644
index 0000000..fc37db1
--- /dev/null
+++ b/_test/data/spec-09-30.canonical
@@ -0,0 +1,7 @@
+%YAML 1.1
+---
+!!str "folded line\n\
+ next line\n\n\
+ \ * bullet\n\
+ \ * list\n\n\
+ last line\n"
diff --git a/_test/data/spec-09-30.data b/_test/data/spec-09-30.data
new file mode 100644
index 0000000..a4d8c36
--- /dev/null
+++ b/_test/data/spec-09-30.data
@@ -0,0 +1,14 @@
+>
+ folded
+ line
+
+ next
+ line
+
+ * bullet
+ * list
+
+ last
+ line
+
+# Comment
diff --git a/_test/data/spec-09-31.canonical b/_test/data/spec-09-31.canonical
new file mode 100644
index 0000000..fc37db1
--- /dev/null
+++ b/_test/data/spec-09-31.canonical
@@ -0,0 +1,7 @@
+%YAML 1.1
+---
+!!str "folded line\n\
+ next line\n\n\
+ \ * bullet\n\
+ \ * list\n\n\
+ last line\n"
diff --git a/_test/data/spec-09-31.data b/_test/data/spec-09-31.data
new file mode 100644
index 0000000..a4d8c36
--- /dev/null
+++ b/_test/data/spec-09-31.data
@@ -0,0 +1,14 @@
+>
+ folded
+ line
+
+ next
+ line
+
+ * bullet
+ * list
+
+ last
+ line
+
+# Comment
diff --git a/_test/data/spec-09-32.canonical b/_test/data/spec-09-32.canonical
new file mode 100644
index 0000000..fc37db1
--- /dev/null
+++ b/_test/data/spec-09-32.canonical
@@ -0,0 +1,7 @@
+%YAML 1.1
+---
+!!str "folded line\n\
+ next line\n\n\
+ \ * bullet\n\
+ \ * list\n\n\
+ last line\n"
diff --git a/_test/data/spec-09-32.data b/_test/data/spec-09-32.data
new file mode 100644
index 0000000..a4d8c36
--- /dev/null
+++ b/_test/data/spec-09-32.data
@@ -0,0 +1,14 @@
+>
+ folded
+ line
+
+ next
+ line
+
+ * bullet
+ * list
+
+ last
+ line
+
+# Comment
diff --git a/_test/data/spec-09-33.canonical b/_test/data/spec-09-33.canonical
new file mode 100644
index 0000000..fc37db1
--- /dev/null
+++ b/_test/data/spec-09-33.canonical
@@ -0,0 +1,7 @@
+%YAML 1.1
+---
+!!str "folded line\n\
+ next line\n\n\
+ \ * bullet\n\
+ \ * list\n\n\
+ last line\n"
diff --git a/_test/data/spec-09-33.data b/_test/data/spec-09-33.data
new file mode 100644
index 0000000..a4d8c36
--- /dev/null
+++ b/_test/data/spec-09-33.data
@@ -0,0 +1,14 @@
+>
+ folded
+ line
+
+ next
+ line
+
+ * bullet
+ * list
+
+ last
+ line
+
+# Comment
diff --git a/_test/data/spec-10-01.canonical b/_test/data/spec-10-01.canonical
new file mode 100644
index 0000000..d08cdd4
--- /dev/null
+++ b/_test/data/spec-10-01.canonical
@@ -0,0 +1,12 @@
+%YAML 1.1
+---
+!!seq [
+ !!seq [
+ !!str "inner",
+ !!str "inner",
+ ],
+ !!seq [
+ !!str "inner",
+ !!str "last",
+ ],
+]
diff --git a/_test/data/spec-10-01.data b/_test/data/spec-10-01.data
new file mode 100644
index 0000000..e668d38
--- /dev/null
+++ b/_test/data/spec-10-01.data
@@ -0,0 +1,2 @@
+- [ inner, inner, ]
+- [inner,last]
diff --git a/_test/data/spec-10-02.canonical b/_test/data/spec-10-02.canonical
new file mode 100644
index 0000000..82fe0d9
--- /dev/null
+++ b/_test/data/spec-10-02.canonical
@@ -0,0 +1,14 @@
+%YAML 1.1
+---
+!!seq [
+ !!str "double quoted",
+ !!str "single quoted",
+ !!str "plain text",
+ !!seq [
+ !!str "nested",
+ ],
+ !!map {
+ ? !!str "single"
+ : !!str "pair"
+ }
+]
diff --git a/_test/data/spec-10-02.data b/_test/data/spec-10-02.data
new file mode 100644
index 0000000..3b23351
--- /dev/null
+++ b/_test/data/spec-10-02.data
@@ -0,0 +1,8 @@
+[
+"double
+ quoted", 'single
+ quoted',
+plain
+ text, [ nested ],
+single: pair ,
+]
diff --git a/_test/data/spec-10-03.canonical b/_test/data/spec-10-03.canonical
new file mode 100644
index 0000000..1443395
--- /dev/null
+++ b/_test/data/spec-10-03.canonical
@@ -0,0 +1,12 @@
+%YAML 1.1
+---
+!!map {
+ ? !!str "block"
+ : !!seq [
+ !!str "one",
+ !!map {
+ ? !!str "two"
+ : !!str "three"
+ }
+ ]
+}
diff --git a/_test/data/spec-10-03.data b/_test/data/spec-10-03.data
new file mode 100644
index 0000000..9e15f83
--- /dev/null
+++ b/_test/data/spec-10-03.data
@@ -0,0 +1,4 @@
+block: # Block
+ # sequence
+- one
+- two : three
diff --git a/_test/data/spec-10-04.canonical b/_test/data/spec-10-04.canonical
new file mode 100644
index 0000000..ae486a3
--- /dev/null
+++ b/_test/data/spec-10-04.canonical
@@ -0,0 +1,11 @@
+%YAML 1.1
+---
+!!map {
+ ? !!str "block"
+ : !!seq [
+ !!str "one",
+ !!seq [
+ !!str "two"
+ ]
+ ]
+}
diff --git a/_test/data/spec-10-04.data b/_test/data/spec-10-04.data
new file mode 100644
index 0000000..2905b0d
--- /dev/null
+++ b/_test/data/spec-10-04.data
@@ -0,0 +1,4 @@
+block:
+- one
+-
+ - two
diff --git a/_test/data/spec-10-05.canonical b/_test/data/spec-10-05.canonical
new file mode 100644
index 0000000..07cc0c9
--- /dev/null
+++ b/_test/data/spec-10-05.canonical
@@ -0,0 +1,14 @@
+%YAML 1.1
+---
+!!seq [
+ !!null "",
+ !!str "block node\n",
+ !!seq [
+ !!str "one",
+ !!str "two",
+ ],
+ !!map {
+ ? !!str "one"
+ : !!str "two",
+ }
+]
diff --git a/_test/data/spec-10-05.data b/_test/data/spec-10-05.data
new file mode 100644
index 0000000..f19a99e
--- /dev/null
+++ b/_test/data/spec-10-05.data
@@ -0,0 +1,7 @@
+- # Empty
+- |
+ block node
+- - one # in-line
+ - two # sequence
+- one: two # in-line
+ # mapping
diff --git a/_test/data/spec-10-06.canonical b/_test/data/spec-10-06.canonical
new file mode 100644
index 0000000..d9986c2
--- /dev/null
+++ b/_test/data/spec-10-06.canonical
@@ -0,0 +1,16 @@
+%YAML 1.1
+---
+!!seq [
+ !!map {
+ ? !!str "inner"
+ : !!str "entry",
+ ? !!str "also"
+ : !!str "inner"
+ },
+ !!map {
+ ? !!str "inner"
+ : !!str "entry",
+ ? !!str "last"
+ : !!str "entry"
+ }
+]
diff --git a/_test/data/spec-10-06.data b/_test/data/spec-10-06.data
new file mode 100644
index 0000000..860ba25
--- /dev/null
+++ b/_test/data/spec-10-06.data
@@ -0,0 +1,2 @@
+- { inner : entry , also: inner , }
+- {inner: entry,last : entry}
diff --git a/_test/data/spec-10-07.canonical b/_test/data/spec-10-07.canonical
new file mode 100644
index 0000000..ec74230
--- /dev/null
+++ b/_test/data/spec-10-07.canonical
@@ -0,0 +1,16 @@
+%YAML 1.1
+---
+!!map {
+ ? !!null ""
+ : !!str "value",
+ ? !!str "explicit key"
+ : !!str "value",
+ ? !!str "simple key"
+ : !!str "value",
+ ? !!seq [
+ !!str "collection",
+ !!str "simple",
+ !!str "key"
+ ]
+ : !!str "value"
+}
diff --git a/_test/data/spec-10-07.data b/_test/data/spec-10-07.data
new file mode 100644
index 0000000..ff943fb
--- /dev/null
+++ b/_test/data/spec-10-07.data
@@ -0,0 +1,7 @@
+{
+? : value, # Empty key
+? explicit
+ key: value,
+simple key : value,
+[ collection, simple, key ]: value
+}
diff --git a/_test/data/spec-10-08.data b/_test/data/spec-10-08.data
new file mode 100644
index 0000000..55bd788
--- /dev/null
+++ b/_test/data/spec-10-08.data
@@ -0,0 +1,5 @@
+{
+multi-line
+ simple key : value,
+very long ...................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................(>1KB)................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................... key: value
+}
diff --git a/_test/data/spec-10-08.error b/_test/data/spec-10-08.error
new file mode 100644
index 0000000..3979e1f
--- /dev/null
+++ b/_test/data/spec-10-08.error
@@ -0,0 +1,5 @@
+ERROR:
+- A simple key is restricted
+ to only one line.
+- A simple key must not be
+ longer than 1024 characters.
diff --git a/_test/data/spec-10-09.canonical b/_test/data/spec-10-09.canonical
new file mode 100644
index 0000000..4d9827b
--- /dev/null
+++ b/_test/data/spec-10-09.canonical
@@ -0,0 +1,8 @@
+%YAML 1.1
+---
+!!map {
+ ? !!str "key"
+ : !!str "value",
+ ? !!str "empty"
+ : !!null "",
+}
diff --git a/_test/data/spec-10-09.data b/_test/data/spec-10-09.data
new file mode 100644
index 0000000..4d55e21
--- /dev/null
+++ b/_test/data/spec-10-09.data
@@ -0,0 +1,4 @@
+{
+key : value,
+empty: # empty value↓
+}
diff --git a/_test/data/spec-10-10.canonical b/_test/data/spec-10-10.canonical
new file mode 100644
index 0000000..016fb64
--- /dev/null
+++ b/_test/data/spec-10-10.canonical
@@ -0,0 +1,16 @@
+%YAML 1.1
+---
+!!map {
+ ? !!str "explicit key1"
+ : !!str "explicit value",
+ ? !!str "explicit key2"
+ : !!null "",
+ ? !!str "explicit key3"
+ : !!null "",
+ ? !!str "simple key1"
+ : !!str "explicit value",
+ ? !!str "simple key2"
+ : !!null "",
+ ? !!str "simple key3"
+ : !!null "",
+}
diff --git a/_test/data/spec-10-10.data b/_test/data/spec-10-10.data
new file mode 100644
index 0000000..0888b05
--- /dev/null
+++ b/_test/data/spec-10-10.data
@@ -0,0 +1,8 @@
+{
+? explicit key1 : explicit value,
+? explicit key2 : , # Explicit empty
+? explicit key3, # Empty value
+simple key1 : explicit value,
+simple key2 : , # Explicit empty
+simple key3, # Empty value
+}
diff --git a/_test/data/spec-10-11.canonical b/_test/data/spec-10-11.canonical
new file mode 100644
index 0000000..7309544
--- /dev/null
+++ b/_test/data/spec-10-11.canonical
@@ -0,0 +1,24 @@
+%YAML 1.1
+---
+!!seq [
+ !!map {
+ ? !!str "explicit key1"
+ : !!str "explicit value",
+ },
+ !!map {
+ ? !!str "explicit key2"
+ : !!null "",
+ },
+ !!map {
+ ? !!str "explicit key3"
+ : !!null "",
+ },
+ !!map {
+ ? !!str "simple key1"
+ : !!str "explicit value",
+ },
+ !!map {
+ ? !!str "simple key2"
+ : !!null "",
+ },
+]
diff --git a/_test/data/spec-10-11.data b/_test/data/spec-10-11.data
new file mode 100644
index 0000000..9f05568
--- /dev/null
+++ b/_test/data/spec-10-11.data
@@ -0,0 +1,7 @@
+[
+? explicit key1 : explicit value,
+? explicit key2 : , # Explicit empty
+? explicit key3, # Implicit empty
+simple key1 : explicit value,
+simple key2 : , # Explicit empty
+]
diff --git a/_test/data/spec-10-12.canonical b/_test/data/spec-10-12.canonical
new file mode 100644
index 0000000..a95dd40
--- /dev/null
+++ b/_test/data/spec-10-12.canonical
@@ -0,0 +1,9 @@
+%YAML 1.1
+---
+!!map {
+ ? !!str "block"
+ : !!map {
+ ? !!str "key"
+ : !!str "value"
+ }
+}
diff --git a/_test/data/spec-10-12.data b/_test/data/spec-10-12.data
new file mode 100644
index 0000000..5521443
--- /dev/null
+++ b/_test/data/spec-10-12.data
@@ -0,0 +1,3 @@
+block: # Block
+ # mapping
+ key: value
diff --git a/_test/data/spec-10-13.canonical b/_test/data/spec-10-13.canonical
new file mode 100644
index 0000000..e183c50
--- /dev/null
+++ b/_test/data/spec-10-13.canonical
@@ -0,0 +1,11 @@
+%YAML 1.1
+---
+!!map {
+ ? !!str "explicit key"
+ : !!null "",
+ ? !!str "block key\n"
+ : !!seq [
+ !!str "one",
+ !!str "two",
+ ]
+}
diff --git a/_test/data/spec-10-13.data b/_test/data/spec-10-13.data
new file mode 100644
index 0000000..b5b97db
--- /dev/null
+++ b/_test/data/spec-10-13.data
@@ -0,0 +1,5 @@
+? explicit key # implicit value
+? |
+ block key
+: - one # explicit in-line
+ - two # block value
diff --git a/_test/data/spec-10-14.canonical b/_test/data/spec-10-14.canonical
new file mode 100644
index 0000000..e87c880
--- /dev/null
+++ b/_test/data/spec-10-14.canonical
@@ -0,0 +1,11 @@
+%YAML 1.1
+---
+!!map {
+ ? !!str "plain key"
+ : !!null "",
+ ? !!str "quoted key"
+ : !!seq [
+ !!str "one",
+ !!str "two",
+ ]
+}
diff --git a/_test/data/spec-10-14.data b/_test/data/spec-10-14.data
new file mode 100644
index 0000000..7f5995c
--- /dev/null
+++ b/_test/data/spec-10-14.data
@@ -0,0 +1,4 @@
+plain key: # empty value
+"quoted key":
+- one # explicit next-line
+- two # block value
diff --git a/_test/data/spec-10-15.canonical b/_test/data/spec-10-15.canonical
new file mode 100644
index 0000000..85fbbd0
--- /dev/null
+++ b/_test/data/spec-10-15.canonical
@@ -0,0 +1,18 @@
+%YAML 1.1
+---
+!!seq [
+ !!map {
+ ? !!str "sun"
+ : !!str "yellow"
+ },
+ !!map {
+ ? !!map {
+ ? !!str "earth"
+ : !!str "blue"
+ }
+ : !!map {
+ ? !!str "moon"
+ : !!str "white"
+ }
+ }
+]
diff --git a/_test/data/spec-10-15.data b/_test/data/spec-10-15.data
new file mode 100644
index 0000000..d675cfd
--- /dev/null
+++ b/_test/data/spec-10-15.data
@@ -0,0 +1,3 @@
+- sun: yellow
+- ? earth: blue
+ : moon: white
diff --git a/_test/data/str.data b/_test/data/str.data
new file mode 100644
index 0000000..7cbdb7c
--- /dev/null
+++ b/_test/data/str.data
@@ -0,0 +1 @@
+- abcd
diff --git a/_test/data/str.detect b/_test/data/str.detect
new file mode 100644
index 0000000..7d5026f
--- /dev/null
+++ b/_test/data/str.detect
@@ -0,0 +1 @@
+tag:yaml.org,2002:str
diff --git a/_test/data/tags.events b/_test/data/tags.events
new file mode 100644
index 0000000..bb93dce
--- /dev/null
+++ b/_test/data/tags.events
@@ -0,0 +1,12 @@
+- !StreamStart
+- !DocumentStart
+- !SequenceStart
+- !Scalar { value: 'data' }
+#- !Scalar { tag: '!', value: 'data' }
+- !Scalar { tag: 'tag:yaml.org,2002:str', value: 'data' }
+- !Scalar { tag: '!myfunnytag', value: 'data' }
+- !Scalar { tag: '!my!ugly!tag', value: 'data' }
+- !Scalar { tag: 'tag:my.domain.org,2002:data!? #', value: 'data' }
+- !SequenceEnd
+- !DocumentEnd
+- !StreamEnd
diff --git a/_test/data/test_mark.marks b/_test/data/test_mark.marks
new file mode 100644
index 0000000..7b08ee4
--- /dev/null
+++ b/_test/data/test_mark.marks
@@ -0,0 +1,38 @@
+---
+*The first line.
+The last line.
+---
+The first*line.
+The last line.
+---
+The first line.*
+The last line.
+---
+The first line.
+*The last line.
+---
+The first line.
+The last*line.
+---
+The first line.
+The last line.*
+---
+The first line.
+*The selected line.
+The last line.
+---
+The first line.
+The selected*line.
+The last line.
+---
+The first line.
+The selected line.*
+The last line.
+---
+*The only line.
+---
+The only*line.
+---
+The only line.*
+---
+Loooooooooooooooooooooooooooooooooooooooooooooong*Liiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiine
diff --git a/_test/data/timestamp-bugs.code b/_test/data/timestamp-bugs.code
new file mode 100644
index 0000000..b1d6e9c
--- /dev/null
+++ b/_test/data/timestamp-bugs.code
@@ -0,0 +1,8 @@
+[
+ datetime.datetime(2001, 12, 15, 3, 29, 43, 100000),
+ datetime.datetime(2001, 12, 14, 16, 29, 43, 100000),
+ datetime.datetime(2001, 12, 14, 21, 59, 43, 1010),
+ datetime.datetime(2001, 12, 14, 21, 59, 43, 0, FixedOffset(60, "+1")),
+ datetime.datetime(2001, 12, 14, 21, 59, 43, 0, FixedOffset(-90, "-1:30")),
+ datetime.datetime(2005, 7, 8, 17, 35, 4, 517600),
+]
diff --git a/_test/data/timestamp-bugs.data b/_test/data/timestamp-bugs.data
new file mode 100644
index 0000000..721d290
--- /dev/null
+++ b/_test/data/timestamp-bugs.data
@@ -0,0 +1,6 @@
+- 2001-12-14 21:59:43.10 -5:30
+- 2001-12-14 21:59:43.10 +5:30
+- 2001-12-14 21:59:43.00101
+- 2001-12-14 21:59:43+1
+- 2001-12-14 21:59:43-1:30
+- 2005-07-08 17:35:04.517600
diff --git a/_test/data/timestamp.data b/_test/data/timestamp.data
new file mode 100644
index 0000000..7d214ce
--- /dev/null
+++ b/_test/data/timestamp.data
@@ -0,0 +1,5 @@
+- 2001-12-15T02:59:43.1Z
+- 2001-12-14t21:59:43.10-05:00
+- 2001-12-14 21:59:43.10 -5
+- 2001-12-15 2:59:43.10
+- 2002-12-14
diff --git a/_test/data/timestamp.detect b/_test/data/timestamp.detect
new file mode 100644
index 0000000..2013936
--- /dev/null
+++ b/_test/data/timestamp.detect
@@ -0,0 +1 @@
+tag:yaml.org,2002:timestamp
diff --git a/_test/data/unclosed-bracket.loader-error b/_test/data/unclosed-bracket.loader-error
new file mode 100644
index 0000000..8c82077
--- /dev/null
+++ b/_test/data/unclosed-bracket.loader-error
@@ -0,0 +1,6 @@
+test:
+ - [ foo: bar
+# comment the rest of the stream to let the scanner detect the problem.
+# - baz
+#"we could have detected the unclosed bracket on the above line, but this would forbid such syntax as": {
+#}
diff --git a/_test/data/unclosed-quoted-scalar.loader-error b/_test/data/unclosed-quoted-scalar.loader-error
new file mode 100644
index 0000000..8537429
--- /dev/null
+++ b/_test/data/unclosed-quoted-scalar.loader-error
@@ -0,0 +1,2 @@
+'foo
+ bar
diff --git a/_test/data/undefined-anchor.loader-error b/_test/data/undefined-anchor.loader-error
new file mode 100644
index 0000000..9469103
--- /dev/null
+++ b/_test/data/undefined-anchor.loader-error
@@ -0,0 +1,3 @@
+- foo
+- &bar baz
+- *bat
diff --git a/_test/data/undefined-constructor.loader-error b/_test/data/undefined-constructor.loader-error
new file mode 100644
index 0000000..9a37ccc
--- /dev/null
+++ b/_test/data/undefined-constructor.loader-error
@@ -0,0 +1 @@
+--- !foo bar
diff --git a/_test/data/undefined-tag-handle.loader-error b/_test/data/undefined-tag-handle.loader-error
new file mode 100644
index 0000000..82ba335
--- /dev/null
+++ b/_test/data/undefined-tag-handle.loader-error
@@ -0,0 +1 @@
+--- !foo!bar baz
diff --git a/_test/data/unknown.dumper-error b/_test/data/unknown.dumper-error
new file mode 100644
index 0000000..83204d2
--- /dev/null
+++ b/_test/data/unknown.dumper-error
@@ -0,0 +1 @@
+yaml.safe_dump(object)
diff --git a/_test/data/unsupported-version.emitter-error b/_test/data/unsupported-version.emitter-error
new file mode 100644
index 0000000..f9c6197
--- /dev/null
+++ b/_test/data/unsupported-version.emitter-error
@@ -0,0 +1,5 @@
+- !StreamStart
+- !DocumentStart { version: [5,6] }
+- !Scalar { value: foo }
+- !DocumentEnd
+- !StreamEnd
diff --git a/_test/data/utf16be.code b/_test/data/utf16be.code
new file mode 100644
index 0000000..c45b371
--- /dev/null
+++ b/_test/data/utf16be.code
@@ -0,0 +1 @@
+"UTF-16-BE"
diff --git a/_test/data/utf16be.data b/_test/data/utf16be.data
new file mode 100644
index 0000000..50dcfae
--- /dev/null
+++ b/_test/data/utf16be.data
Binary files differ
diff --git a/_test/data/utf16le.code b/_test/data/utf16le.code
new file mode 100644
index 0000000..400530a
--- /dev/null
+++ b/_test/data/utf16le.code
@@ -0,0 +1 @@
+"UTF-16-LE"
diff --git a/_test/data/utf16le.data b/_test/data/utf16le.data
new file mode 100644
index 0000000..76f5e73
--- /dev/null
+++ b/_test/data/utf16le.data
Binary files differ
diff --git a/_test/data/utf8-implicit.code b/_test/data/utf8-implicit.code
new file mode 100644
index 0000000..29326db
--- /dev/null
+++ b/_test/data/utf8-implicit.code
@@ -0,0 +1 @@
+"implicit UTF-8"
diff --git a/_test/data/utf8-implicit.data b/_test/data/utf8-implicit.data
new file mode 100644
index 0000000..9d8081e
--- /dev/null
+++ b/_test/data/utf8-implicit.data
@@ -0,0 +1 @@
+--- implicit UTF-8
diff --git a/_test/data/utf8.code b/_test/data/utf8.code
new file mode 100644
index 0000000..dcf11cc
--- /dev/null
+++ b/_test/data/utf8.code
@@ -0,0 +1 @@
+"UTF-8"
diff --git a/_test/data/utf8.data b/_test/data/utf8.data
new file mode 100644
index 0000000..686f48a
--- /dev/null
+++ b/_test/data/utf8.data
@@ -0,0 +1 @@
+--- UTF-8
diff --git a/_test/data/util/00_ok.yaml b/_test/data/util/00_ok.yaml
new file mode 100644
index 0000000..adc4adf
--- /dev/null
+++ b/_test/data/util/00_ok.yaml
@@ -0,0 +1,3 @@
+- abc
+- ghi # some comment
+- klm
diff --git a/_test/data/util/01_second_rt_ok.yaml b/_test/data/util/01_second_rt_ok.yaml
new file mode 100644
index 0000000..de19513
--- /dev/null
+++ b/_test/data/util/01_second_rt_ok.yaml
@@ -0,0 +1,3 @@
+- abc
+- ghi # some comment
+- klm
diff --git a/_test/data/util/02_not_ok.yaml b/_test/data/util/02_not_ok.yaml
new file mode 100644
index 0000000..945e5ec
--- /dev/null
+++ b/_test/data/util/02_not_ok.yaml
@@ -0,0 +1,2 @@
+123 # single scalar cannot have comment
+...
diff --git a/_test/data/util/03_no_comment_ok.yaml b/_test/data/util/03_no_comment_ok.yaml
new file mode 100644
index 0000000..081284a
--- /dev/null
+++ b/_test/data/util/03_no_comment_ok.yaml
@@ -0,0 +1,2 @@
+123
+...
diff --git a/_test/data/valid_escape_characters.code b/_test/data/valid_escape_characters.code
new file mode 100644
index 0000000..0434f0c
--- /dev/null
+++ b/_test/data/valid_escape_characters.code
@@ -0,0 +1 @@
+"\" \\ / \b \f \n \r \t"
diff --git a/_test/data/valid_escape_characters.data b/_test/data/valid_escape_characters.data
new file mode 100644
index 0000000..a28e216
--- /dev/null
+++ b/_test/data/valid_escape_characters.data
@@ -0,0 +1 @@
+"\" \\ \/ \b \f \n \r \t"
diff --git a/_test/data/valid_escape_characters.skip-ext b/_test/data/valid_escape_characters.skip-ext
new file mode 100644
index 0000000..e69de29
--- /dev/null
+++ b/_test/data/valid_escape_characters.skip-ext
diff --git a/_test/data/value.data b/_test/data/value.data
new file mode 100644
index 0000000..c5b7680
--- /dev/null
+++ b/_test/data/value.data
@@ -0,0 +1 @@
+- =
diff --git a/_test/data/value.detect b/_test/data/value.detect
new file mode 100644
index 0000000..7c37d02
--- /dev/null
+++ b/_test/data/value.detect
@@ -0,0 +1 @@
+tag:yaml.org,2002:value
diff --git a/_test/data/yaml.data b/_test/data/yaml.data
new file mode 100644
index 0000000..a4bb3f8
--- /dev/null
+++ b/_test/data/yaml.data
@@ -0,0 +1,3 @@
+- !!yaml '!'
+- !!yaml '&'
+- !!yaml '*'
diff --git a/_test/data/yaml.detect b/_test/data/yaml.detect
new file mode 100644
index 0000000..e2cf189
--- /dev/null
+++ b/_test/data/yaml.detect
@@ -0,0 +1 @@
+tag:yaml.org,2002:yaml
diff --git a/_test/lib/canonical.py b/_test/lib/canonical.py
new file mode 100644
index 0000000..31c9728
--- /dev/null
+++ b/_test/lib/canonical.py
@@ -0,0 +1,388 @@
+
+import ruamel.yaml
+from ruamel.yaml.composer import Composer
+from ruamel.yaml.constructor import Constructor
+from ruamel.yaml.resolver import Resolver
+
+
+class CanonicalError(ruamel.yaml.YAMLError):
+ pass
+
+
+class CanonicalScanner:
+ def __init__(self, data):
+ try:
+ if isinstance(data, bytes):
+ data = data.decode('utf-8')
+ except UnicodeDecodeError:
+ raise CanonicalError('utf-8 stream is expected')
+ self.data = data + '\0'
+ self.index = 0
+ self.tokens = []
+ self.scanned = False
+
+ def check_token(self, *choices):
+ if not self.scanned:
+ self.scan()
+ if self.tokens:
+ if not choices:
+ return True
+ for choice in choices:
+ if isinstance(self.tokens[0], choice):
+ return True
+ return False
+
+ def peek_token(self):
+ if not self.scanned:
+ self.scan()
+ if self.tokens:
+ return self.tokens[0]
+
+ def get_token(self, choice=None):
+ if not self.scanned:
+ self.scan()
+ token = self.tokens.pop(0)
+ if choice and not isinstance(token, choice):
+ raise CanonicalError('unexpected token ' + repr(token))
+ return token
+
+ def get_token_value(self):
+ token = self.get_token()
+ return token.value
+
+ def scan(self):
+ self.tokens.append(ruamel.yaml.StreamStartToken(None, None))
+ while True:
+ self.find_token()
+ ch = self.data[self.index]
+ if ch == '\0':
+ self.tokens.append(ruamel.yaml.StreamEndToken(None, None))
+ break
+ elif ch == '%':
+ self.tokens.append(self.scan_directive())
+ elif ch == '-' and self.data[self.index : self.index + 3] == '---':
+ self.index += 3
+ self.tokens.append(ruamel.yaml.DocumentStartToken(None, None))
+ elif ch == '[':
+ self.index += 1
+ self.tokens.append(ruamel.yaml.FlowSequenceStartToken(None, None))
+ elif ch == '{':
+ self.index += 1
+ self.tokens.append(ruamel.yaml.FlowMappingStartToken(None, None))
+ elif ch == ']':
+ self.index += 1
+ self.tokens.append(ruamel.yaml.FlowSequenceEndToken(None, None))
+ elif ch == '}':
+ self.index += 1
+ self.tokens.append(ruamel.yaml.FlowMappingEndToken(None, None))
+ elif ch == '?':
+ self.index += 1
+ self.tokens.append(ruamel.yaml.KeyToken(None, None))
+ elif ch == ':':
+ self.index += 1
+ self.tokens.append(ruamel.yaml.ValueToken(None, None))
+ elif ch == ',':
+ self.index += 1
+ self.tokens.append(ruamel.yaml.FlowEntryToken(None, None))
+ elif ch == '*' or ch == '&':
+ self.tokens.append(self.scan_alias())
+ elif ch == '!':
+ self.tokens.append(self.scan_tag())
+ elif ch == '"':
+ self.tokens.append(self.scan_scalar())
+ else:
+ raise CanonicalError('invalid token')
+ self.scanned = True
+
+ DIRECTIVE = '%YAML 1.1'
+
+ def scan_directive(self):
+ if (
+ self.data[self.index : self.index + len(self.DIRECTIVE)] == self.DIRECTIVE
+ and self.data[self.index + len(self.DIRECTIVE)] in ' \n\0'
+ ):
+ self.index += len(self.DIRECTIVE)
+ return ruamel.yaml.DirectiveToken('YAML', (1, 1), None, None)
+ else:
+ raise CanonicalError('invalid directive')
+
+ def scan_alias(self):
+ if self.data[self.index] == '*':
+ TokenClass = ruamel.yaml.AliasToken
+ else:
+ TokenClass = ruamel.yaml.AnchorToken
+ self.index += 1
+ start = self.index
+ while self.data[self.index] not in ', \n\0':
+ self.index += 1
+ value = self.data[start : self.index]
+ return TokenClass(value, None, None)
+
+ def scan_tag(self):
+ self.index += 1
+ start = self.index
+ while self.data[self.index] not in ' \n\0':
+ self.index += 1
+ value = self.data[start : self.index]
+ if not value:
+ value = '!'
+ elif value[0] == '!':
+ value = 'tag:yaml.org,2002:' + value[1:]
+ elif value[0] == '<' and value[-1] == '>':
+ value = value[1:-1]
+ else:
+ value = '!' + value
+ return ruamel.yaml.TagToken(value, None, None)
+
+ QUOTE_CODES = {'x': 2, 'u': 4, 'U': 8}
+
+ QUOTE_REPLACES = {
+ '\\': '\\',
+ '"': '"',
+ ' ': ' ',
+ 'a': '\x07',
+ 'b': '\x08',
+ 'e': '\x1B',
+ 'f': '\x0C',
+ 'n': '\x0A',
+ 'r': '\x0D',
+ 't': '\x09',
+ 'v': '\x0B',
+ 'N': '\u0085',
+ 'L': '\u2028',
+ 'P': '\u2029',
+ '_': '_',
+ '0': '\x00',
+ }
+
+ def scan_scalar(self):
+ self.index += 1
+ chunks = []
+ start = self.index
+ ignore_spaces = False
+ while self.data[self.index] != '"':
+ if self.data[self.index] == '\\':
+ ignore_spaces = False
+ chunks.append(self.data[start : self.index])
+ self.index += 1
+ ch = self.data[self.index]
+ self.index += 1
+ if ch == '\n':
+ ignore_spaces = True
+ elif ch in self.QUOTE_CODES:
+ length = self.QUOTE_CODES[ch]
+ code = int(self.data[self.index : self.index + length], 16)
+ chunks.append(chr(code))
+ self.index += length
+ else:
+ if ch not in self.QUOTE_REPLACES:
+ raise CanonicalError('invalid escape code')
+ chunks.append(self.QUOTE_REPLACES[ch])
+ start = self.index
+ elif self.data[self.index] == '\n':
+ chunks.append(self.data[start : self.index])
+ chunks.append(' ')
+ self.index += 1
+ start = self.index
+ ignore_spaces = True
+ elif ignore_spaces and self.data[self.index] == ' ':
+ self.index += 1
+ start = self.index
+ else:
+ ignore_spaces = False
+ self.index += 1
+ chunks.append(self.data[start : self.index])
+ self.index += 1
+ return ruamel.yaml.ScalarToken("".join(chunks), False, None, None)
+
+ def find_token(self):
+ found = False
+ while not found:
+ while self.data[self.index] in ' \t':
+ self.index += 1
+ if self.data[self.index] == '#':
+ while self.data[self.index] != '\n':
+ self.index += 1
+ if self.data[self.index] == '\n':
+ self.index += 1
+ else:
+ found = True
+
+
+class CanonicalParser:
+ def __init__(self):
+ self.events = []
+ self.parsed = False
+
+ def dispose(self):
+ pass
+
+ # stream: STREAM-START document* STREAM-END
+ def parse_stream(self):
+ self.get_token(ruamel.yaml.StreamStartToken)
+ self.events.append(ruamel.yaml.StreamStartEvent(None, None))
+ while not self.check_token(ruamel.yaml.StreamEndToken):
+ if self.check_token(ruamel.yaml.DirectiveToken, ruamel.yaml.DocumentStartToken):
+ self.parse_document()
+ else:
+ raise CanonicalError('document is expected, got ' + repr(self.tokens[0]))
+ self.get_token(ruamel.yaml.StreamEndToken)
+ self.events.append(ruamel.yaml.StreamEndEvent(None, None))
+
+ # document: DIRECTIVE? DOCUMENT-START node
+ def parse_document(self):
+ # node = None
+ if self.check_token(ruamel.yaml.DirectiveToken):
+ self.get_token(ruamel.yaml.DirectiveToken)
+ self.get_token(ruamel.yaml.DocumentStartToken)
+ self.events.append(ruamel.yaml.DocumentStartEvent(None, None))
+ self.parse_node()
+ self.events.append(ruamel.yaml.DocumentEndEvent(None, None))
+
+ # node: ALIAS | ANCHOR? TAG? (SCALAR|sequence|mapping)
+ def parse_node(self):
+ if self.check_token(ruamel.yaml.AliasToken):
+ self.events.append(ruamel.yaml.AliasEvent(self.get_token_value(), None, None))
+ else:
+ anchor = None
+ if self.check_token(ruamel.yaml.AnchorToken):
+ anchor = self.get_token_value()
+ tag = None
+ if self.check_token(ruamel.yaml.TagToken):
+ tag = self.get_token_value()
+ if self.check_token(ruamel.yaml.ScalarToken):
+ self.events.append(
+ ruamel.yaml.ScalarEvent(
+ anchor, tag, (False, False), self.get_token_value(), None, None
+ )
+ )
+ elif self.check_token(ruamel.yaml.FlowSequenceStartToken):
+ self.events.append(ruamel.yaml.SequenceStartEvent(anchor, tag, None, None))
+ self.parse_sequence()
+ elif self.check_token(ruamel.yaml.FlowMappingStartToken):
+ self.events.append(ruamel.yaml.MappingStartEvent(anchor, tag, None, None))
+ self.parse_mapping()
+ else:
+ raise CanonicalError(
+ "SCALAR, '[', or '{' is expected, got " + repr(self.tokens[0])
+ )
+
+ # sequence: SEQUENCE-START (node (ENTRY node)*)? ENTRY? SEQUENCE-END
+ def parse_sequence(self):
+ self.get_token(ruamel.yaml.FlowSequenceStartToken)
+ if not self.check_token(ruamel.yaml.FlowSequenceEndToken):
+ self.parse_node()
+ while not self.check_token(ruamel.yaml.FlowSequenceEndToken):
+ self.get_token(ruamel.yaml.FlowEntryToken)
+ if not self.check_token(ruamel.yaml.FlowSequenceEndToken):
+ self.parse_node()
+ self.get_token(ruamel.yaml.FlowSequenceEndToken)
+ self.events.append(ruamel.yaml.SequenceEndEvent(None, None))
+
+ # mapping: MAPPING-START (map_entry (ENTRY map_entry)*)? ENTRY? MAPPING-END
+ def parse_mapping(self):
+ self.get_token(ruamel.yaml.FlowMappingStartToken)
+ if not self.check_token(ruamel.yaml.FlowMappingEndToken):
+ self.parse_map_entry()
+ while not self.check_token(ruamel.yaml.FlowMappingEndToken):
+ self.get_token(ruamel.yaml.FlowEntryToken)
+ if not self.check_token(ruamel.yaml.FlowMappingEndToken):
+ self.parse_map_entry()
+ self.get_token(ruamel.yaml.FlowMappingEndToken)
+ self.events.append(ruamel.yaml.MappingEndEvent(None, None))
+
+ # map_entry: KEY node VALUE node
+ def parse_map_entry(self):
+ self.get_token(ruamel.yaml.KeyToken)
+ self.parse_node()
+ self.get_token(ruamel.yaml.ValueToken)
+ self.parse_node()
+
+ def parse(self):
+ self.parse_stream()
+ self.parsed = True
+
+ def get_event(self):
+ if not self.parsed:
+ self.parse()
+ return self.events.pop(0)
+
+ def check_event(self, *choices):
+ if not self.parsed:
+ self.parse()
+ if self.events:
+ if not choices:
+ return True
+ for choice in choices:
+ if isinstance(self.events[0], choice):
+ return True
+ return False
+
+ def peek_event(self):
+ if not self.parsed:
+ self.parse()
+ return self.events[0]
+
+
+class CanonicalLoader(CanonicalScanner, CanonicalParser, Composer, Constructor, Resolver):
+ def __init__(self, stream):
+ if hasattr(stream, 'read'):
+ stream = stream.read()
+ CanonicalScanner.__init__(self, stream)
+ CanonicalParser.__init__(self)
+ Composer.__init__(self)
+ Constructor.__init__(self)
+ Resolver.__init__(self)
+
+
+ruamel.yaml.CanonicalLoader = CanonicalLoader
+
+
+def canonical_scan(stream):
+ yaml = ruamel.yaml.YAML()
+ yaml.scanner = CanonicalScanner
+ return yaml.scan(stream)
+
+
+ruamel.yaml.canonical_scan = canonical_scan
+
+
+def canonical_parse(stream):
+ yaml = ruamel.yaml.YAML()
+ return yaml.parse(stream, Loader=CanonicalLoader)
+
+
+ruamel.yaml.canonical_parse = canonical_parse
+
+
+def canonical_compose(stream):
+ yaml = ruamel.yaml.YAML()
+ return yaml.compose(stream, Loader=CanonicalLoader)
+
+
+ruamel.yaml.canonical_compose = canonical_compose
+
+
+def canonical_compose_all(stream):
+ yaml = ruamel.yaml.YAML()
+ return yaml.compose_all(stream, Loader=CanonicalLoader)
+
+
+ruamel.yaml.canonical_compose_all = canonical_compose_all
+
+
+def canonical_load(stream):
+ yaml = ruamel.yaml.YAML()
+ return yaml.load(stream, Loader=CanonicalLoader)
+
+
+ruamel.yaml.canonical_load = canonical_load
+
+
+def canonical_load_all(stream):
+ yaml = ruamel.yaml.YAML(typ='safe', pure=True)
+ yaml.Loader = CanonicalLoader
+ return yaml.load_all(stream)
+
+
+ruamel.yaml.canonical_load_all = canonical_load_all
diff --git a/_test/lib/test_all.py b/_test/lib/test_all.py
new file mode 100644
index 0000000..8099ec8
--- /dev/null
+++ b/_test/lib/test_all.py
@@ -0,0 +1,20 @@
+
+import sys # NOQA
+import ruamel.yaml
+import test_appliance
+
+
+def main(args=None):
+ collections = []
+ import test_yaml
+
+ collections.append(test_yaml)
+ if ruamel.yaml.__with_libyaml__:
+ import test_yaml_ext
+
+ collections.append(test_yaml_ext)
+ test_appliance.run(collections, args)
+
+
+if __name__ == '__main__':
+ main()
diff --git a/_test/lib/test_appliance.py b/_test/lib/test_appliance.py
new file mode 100644
index 0000000..d624ebe
--- /dev/null
+++ b/_test/lib/test_appliance.py
@@ -0,0 +1,209 @@
+
+import sys
+import os
+import types
+import traceback
+import pprint
+import argparse
+
+# DATA = 'tests/data'
+# determine the position of data dynamically relative to program
+# this allows running test while the current path is not the top of the
+# repository, e.g. from the tests/data directory: python ../test_yaml.py
+DATA = __file__.rsplit(os.sep, 2)[0] + '/data'
+
+
+def find_test_functions(collections):
+ if not isinstance(collections, list):
+ collections = [collections]
+ functions = []
+ for collection in collections:
+ if not isinstance(collection, dict):
+ collection = vars(collection)
+ for key in sorted(collection):
+ value = collection[key]
+ if isinstance(value, types.FunctionType) and hasattr(value, 'unittest'):
+ functions.append(value)
+ return functions
+
+
+def find_test_filenames(directory):
+ filenames = {}
+ for filename in os.listdir(directory):
+ if os.path.isfile(os.path.join(directory, filename)):
+ base, ext = os.path.splitext(filename)
+ # ToDo: remove
+ if base.endswith('-py2'):
+ continue
+ filenames.setdefault(base, []).append(ext)
+ filenames = sorted(filenames.items())
+ return filenames
+
+
+def parse_arguments(args):
+ """"""
+ parser = argparse.ArgumentParser(
+ usage=""" run the yaml tests. By default
+ all functions on all appropriate test_files are run. Functions have
+ unittest attributes that determine the required extensions to filenames
+ that need to be available in order to run that test. E.g.\n\n
+ python test_yaml.py test_constructor_types\n
+ python test_yaml.py --verbose test_tokens spec-02-05\n\n
+ The presence of an extension in the .skip attribute of a function
+ disables the test for that function."""
+ )
+ # ToDo: make into int and test > 0 in functions
+ parser.add_argument(
+ '--verbose',
+ '-v',
+ action='store_true',
+ default='YAML_TEST_VERBOSE' in os.environ,
+ help='set verbosity output',
+ )
+ parser.add_argument(
+ '--list-functions',
+ action='store_true',
+ help="""list all functions with required file extensions for test files
+ """,
+ )
+ parser.add_argument('function', nargs='?', help="""restrict function to run""")
+ parser.add_argument(
+ 'filenames',
+ nargs='*',
+ help="""basename of filename set, extensions (.code, .data) have to
+ be a superset of those in the unittest attribute of the selected
+ function""",
+ )
+ args = parser.parse_args(args)
+ # print('args', args)
+ verbose = args.verbose
+ include_functions = [args.function] if args.function else []
+ include_filenames = args.filenames
+ # if args is None:
+ # args = sys.argv[1:]
+ # verbose = False
+ # if '-v' in args:
+ # verbose = True
+ # args.remove('-v')
+ # if '--verbose' in args:
+ # verbose = True
+ # args.remove('--verbose') # never worked without this
+ # if 'YAML_TEST_VERBOSE' in os.environ:
+ # verbose = True
+ # include_functions = []
+ # if args:
+ # include_functions.append(args.pop(0))
+ if 'YAML_TEST_FUNCTIONS' in os.environ:
+ include_functions.extend(os.environ['YAML_TEST_FUNCTIONS'].split())
+ # include_filenames = []
+ # include_filenames.extend(args)
+ if 'YAML_TEST_FILENAMES' in os.environ:
+ include_filenames.extend(os.environ['YAML_TEST_FILENAMES'].split())
+ return include_functions, include_filenames, verbose, args
+
+
+def execute(function, filenames, verbose):
+ name = function.__name__
+ if verbose:
+ sys.stdout.write('=' * 75 + '\n')
+ sys.stdout.write('%s(%s)...\n' % (name, ', '.join(filenames)))
+ try:
+ function(verbose=verbose, *filenames)
+ except Exception as exc:
+ info = sys.exc_info()
+ if isinstance(exc, AssertionError):
+ kind = 'FAILURE'
+ else:
+ kind = 'ERROR'
+ if verbose:
+ traceback.print_exc(limit=1, file=sys.stdout)
+ else:
+ sys.stdout.write(kind[0])
+ sys.stdout.flush()
+ else:
+ kind = 'SUCCESS'
+ info = None
+ if not verbose:
+ sys.stdout.write('.')
+ sys.stdout.flush()
+ return (name, filenames, kind, info)
+
+
+def display(results, verbose):
+ if results and not verbose:
+ sys.stdout.write('\n')
+ total = len(results)
+ failures = 0
+ errors = 0
+ for name, filenames, kind, info in results:
+ if kind == 'SUCCESS':
+ continue
+ if kind == 'FAILURE':
+ failures += 1
+ if kind == 'ERROR':
+ errors += 1
+ sys.stdout.write('=' * 75 + '\n')
+ sys.stdout.write('%s(%s): %s\n' % (name, ', '.join(filenames), kind))
+ if kind == 'ERROR':
+ traceback.print_exception(file=sys.stdout, *info)
+ else:
+ sys.stdout.write('Traceback (most recent call last):\n')
+ traceback.print_tb(info[2], file=sys.stdout)
+ sys.stdout.write('%s: see below\n' % info[0].__name__)
+ sys.stdout.write('~' * 75 + '\n')
+ for arg in info[1].args:
+ pprint.pprint(arg, stream=sys.stdout)
+ for filename in filenames:
+ sys.stdout.write('-' * 75 + '\n')
+ sys.stdout.write('%s:\n' % filename)
+ with open(filename, 'r', errors='replace') as fp:
+ data = fp.read()
+ sys.stdout.write(data)
+ if data and data[-1] != '\n':
+ sys.stdout.write('\n')
+ sys.stdout.write('=' * 75 + '\n')
+ sys.stdout.write('TESTS: %s\n' % total)
+ ret_val = 0
+ if failures:
+ sys.stdout.write('FAILURES: %s\n' % failures)
+ ret_val = 1
+ if errors:
+ sys.stdout.write('ERRORS: %s\n' % errors)
+ ret_val = 2
+ return ret_val
+
+
+def run(collections, args=None):
+ test_functions = find_test_functions(collections)
+ test_filenames = find_test_filenames(DATA)
+ include_functions, include_filenames, verbose, a = parse_arguments(args)
+ if a.list_functions:
+ print('test functions:')
+ for f in test_functions:
+ print(' {:30s} {}'.format(f.__name__, f.unittest))
+ return
+ results = []
+ for function in test_functions:
+ if include_functions and function.__name__ not in include_functions:
+ continue
+ if function.unittest:
+ for base, exts in test_filenames:
+ if include_filenames and base not in include_filenames:
+ continue
+ filenames = []
+ for ext in function.unittest:
+ if ext not in exts:
+ break
+ filenames.append(os.path.join(DATA, base + ext))
+ else:
+ skip_exts = getattr(function, 'skip', [])
+ for skip_ext in skip_exts:
+ if skip_ext in exts:
+ break
+ else:
+ result = execute(function, filenames, verbose)
+ results.append(result)
+ else:
+ result = execute(function, [], verbose)
+ results.append(result)
+ return display(results, verbose=verbose)
diff --git a/_test/lib/test_build.py b/_test/lib/test_build.py
new file mode 100644
index 0000000..f7837eb
--- /dev/null
+++ b/_test/lib/test_build.py
@@ -0,0 +1,16 @@
+
+if __name__ == '__main__':
+ import sys
+ import os
+ import distutils.util
+
+ build_lib = 'build/lib'
+ build_lib_ext = os.path.join(
+ 'build', 'lib.%s-%s' % (distutils.util.get_platform(), sys.version[0:3])
+ )
+ sys.path.insert(0, build_lib)
+ sys.path.insert(0, build_lib_ext)
+ import test_yaml
+ import test_appliance
+
+ test_appliance.run(test_yaml)
diff --git a/_test/lib/test_build_ext.py b/_test/lib/test_build_ext.py
new file mode 100644
index 0000000..1a58fd2
--- /dev/null
+++ b/_test/lib/test_build_ext.py
@@ -0,0 +1,17 @@
+
+
+if __name__ == '__main__':
+ import sys
+ import os
+ import distutils.util
+
+ build_lib = 'build/lib'
+ build_lib_ext = os.path.join(
+ 'build', 'lib.%s-%s' % (distutils.util.get_platform(), sys.version[0:3])
+ )
+ sys.path.insert(0, build_lib)
+ sys.path.insert(0, build_lib_ext)
+ import test_yaml_ext
+ import test_appliance
+
+ test_appliance.run(test_yaml_ext)
diff --git a/_test/lib/test_canonical.py b/_test/lib/test_canonical.py
new file mode 100644
index 0000000..b5cd14d
--- /dev/null
+++ b/_test/lib/test_canonical.py
@@ -0,0 +1,51 @@
+
+
+import ruamel.yaml
+import canonical # NOQA
+
+
+def test_canonical_scanner(canonical_filename, verbose=False):
+ with open(canonical_filename, 'rb') as fp0:
+ data = fp0.read()
+ tokens = list(ruamel.yaml.canonical_scan(data))
+ assert tokens, tokens
+ if verbose:
+ for token in tokens:
+ print(token)
+
+
+test_canonical_scanner.unittest = ['.canonical']
+
+
+def test_canonical_parser(canonical_filename, verbose=False):
+ with open(canonical_filename, 'rb') as fp0:
+ data = fp0.read()
+ events = list(ruamel.yaml.canonical_parse(data))
+ assert events, events
+ if verbose:
+ for event in events:
+ print(event)
+
+
+test_canonical_parser.unittest = ['.canonical']
+
+
+def test_canonical_error(data_filename, canonical_filename, verbose=False):
+ with open(data_filename, 'rb') as fp0:
+ data = fp0.read()
+ try:
+ output = list(ruamel.yaml.canonical_load_all(data)) # NOQA
+ except ruamel.yaml.YAMLError as exc:
+ if verbose:
+ print(exc)
+ else:
+ raise AssertionError('expected an exception')
+
+
+test_canonical_error.unittest = ['.data', '.canonical']
+test_canonical_error.skip = ['.empty']
+
+if __name__ == '__main__':
+ import test_appliance
+
+ test_appliance.run(globals())
diff --git a/_test/lib/test_constructor.py b/_test/lib/test_constructor.py
new file mode 100644
index 0000000..b38bf2f
--- /dev/null
+++ b/_test/lib/test_constructor.py
@@ -0,0 +1,372 @@
+
+import ruamel.yaml
+import pprint
+
+import datetime
+
+try:
+ set
+except NameError:
+ from sets import Set as set # NOQA
+import ruamel.yaml.tokens
+
+
+def execute(code):
+ global value
+ exec(code)
+ return value
+
+
+def _make_objects():
+ global MyLoader, MyDumper, MyTestClass1, MyTestClass2, MyTestClass3, YAMLobject1, YAMLobject2, AnObject, AnInstance, AState, ACustomState, InitArgs, InitArgsWithState, NewArgs, NewArgsWithState, Reduce, ReduceWithState, MyInt, MyList, MyDict, FixedOffset, today, execute
+
+ class MyLoader(ruamel.yaml.Loader):
+ pass
+
+ class MyDumper(ruamel.yaml.Dumper):
+ pass
+
+ class MyTestClass1:
+ def __init__(self, x, y=0, z=0):
+ self.x = x
+ self.y = y
+ self.z = z
+
+ def __eq__(self, other):
+ if isinstance(other, MyTestClass1):
+ return self.__class__, self.__dict__ == other.__class__, other.__dict__
+ else:
+ return False
+
+ def construct1(constructor, node):
+ mapping = constructor.construct_mapping(node)
+ return MyTestClass1(**mapping)
+
+ def represent1(representer, native):
+ return representer.represent_mapping('!tag1', native.__dict__)
+
+ ruamel.yaml.add_constructor('!tag1', construct1, Loader=MyLoader)
+ ruamel.yaml.add_representer(MyTestClass1, represent1, Dumper=MyDumper)
+
+ class MyTestClass2(MyTestClass1, ruamel.yaml.YAMLObject):
+ ruamel.yaml.loader = MyLoader
+ ruamel.yaml.dumper = MyDumper
+ ruamel.yaml.tag = '!tag2'
+
+ def from_yaml(cls, constructor, node):
+ x = constructor.construct_yaml_int(node)
+ return cls(x=x)
+
+ from_yaml = classmethod(from_yaml)
+
+ def to_yaml(cls, representer, native):
+ return representer.represent_scalar(cls.yaml_tag, str(native.x))
+
+ to_yaml = classmethod(to_yaml)
+
+ class MyTestClass3(MyTestClass2):
+ ruamel.yaml.tag = '!tag3'
+
+ def from_yaml(cls, constructor, node):
+ mapping = constructor.construct_mapping(node)
+ if '=' in mapping:
+ x = mapping['=']
+ del mapping['=']
+ mapping['x'] = x
+ return cls(**mapping)
+
+ from_yaml = classmethod(from_yaml)
+
+ def to_yaml(cls, representer, native):
+ return representer.represent_mapping(cls.yaml_tag, native.__dict__)
+
+ to_yaml = classmethod(to_yaml)
+
+ class YAMLobject1(ruamel.yaml.YAMLObject):
+ ruamel.yaml.loader = MyLoader
+ ruamel.yaml.dumper = MyDumper
+ ruamel.yaml.tag = '!foo'
+
+ def __init__(self, my_parameter=None, my_another_parameter=None):
+ self.my_parameter = my_parameter
+ self.my_another_parameter = my_another_parameter
+
+ def __eq__(self, other):
+ if isinstance(other, YAMLobject1):
+ return self.__class__, self.__dict__ == other.__class__, other.__dict__
+ else:
+ return False
+
+ class YAMLobject2(ruamel.yaml.YAMLObject):
+ ruamel.yaml.loader = MyLoader
+ ruamel.yaml.dumper = MyDumper
+ ruamel.yaml.tag = '!bar'
+
+ def __init__(self, foo=1, bar=2, baz=3):
+ self.foo = foo
+ self.bar = bar
+ self.baz = baz
+
+ def __getstate__(self):
+ return {1: self.foo, 2: self.bar, 3: self.baz}
+
+ def __setstate__(self, state):
+ self.foo = state[1]
+ self.bar = state[2]
+ self.baz = state[3]
+
+ def __eq__(self, other):
+ if isinstance(other, YAMLobject2):
+ return self.__class__, self.__dict__ == other.__class__, other.__dict__
+ else:
+ return False
+
+ class AnObject:
+ def __new__(cls, foo=None, bar=None, baz=None):
+ self = object.__new__(cls)
+ self.foo = foo
+ self.bar = bar
+ self.baz = baz
+ return self
+
+ def __cmp__(self, other):
+ return cmp(
+ (type(self), self.foo, self.bar, self.baz), # NOQA
+ (type(other), other.foo, other.bar, other.baz),
+ )
+
+ def __eq__(self, other):
+ return type(self) is type(other) and (self.foo, self.bar, self.baz) == (
+ other.foo,
+ other.bar,
+ other.baz,
+ )
+
+ class AnInstance:
+ def __init__(self, foo=None, bar=None, baz=None):
+ self.foo = foo
+ self.bar = bar
+ self.baz = baz
+
+ def __cmp__(self, other):
+ return cmp(
+ (type(self), self.foo, self.bar, self.baz), # NOQA
+ (type(other), other.foo, other.bar, other.baz),
+ )
+
+ def __eq__(self, other):
+ return type(self) is type(other) and (self.foo, self.bar, self.baz) == (
+ other.foo,
+ other.bar,
+ other.baz,
+ )
+
+ class AState(AnInstance):
+ def __getstate__(self):
+ return {'_foo': self.foo, '_bar': self.bar, '_baz': self.baz}
+
+ def __setstate__(self, state):
+ self.foo = state['_foo']
+ self.bar = state['_bar']
+ self.baz = state['_baz']
+
+ class ACustomState(AnInstance):
+ def __getstate__(self):
+ return (self.foo, self.bar, self.baz)
+
+ def __setstate__(self, state):
+ self.foo, self.bar, self.baz = state
+
+ # class InitArgs(AnInstance):
+ # def __getinitargs__(self):
+ # return (self.foo, self.bar, self.baz)
+ # def __getstate__(self):
+ # return {}
+
+ # class InitArgsWithState(AnInstance):
+ # def __getinitargs__(self):
+ # return (self.foo, self.bar)
+ # def __getstate__(self):
+ # return self.baz
+ # def __setstate__(self, state):
+ # self.baz = state
+
+ class NewArgs(AnObject):
+ def __getnewargs__(self):
+ return (self.foo, self.bar, self.baz)
+
+ def __getstate__(self):
+ return {}
+
+ class NewArgsWithState(AnObject):
+ def __getnewargs__(self):
+ return (self.foo, self.bar)
+
+ def __getstate__(self):
+ return self.baz
+
+ def __setstate__(self, state):
+ self.baz = state
+
+ InitArgs = NewArgs
+
+ InitArgsWithState = NewArgsWithState
+
+ class Reduce(AnObject):
+ def __reduce__(self):
+ return self.__class__, (self.foo, self.bar, self.baz)
+
+ class ReduceWithState(AnObject):
+ def __reduce__(self):
+ return self.__class__, (self.foo, self.bar), self.baz
+
+ def __setstate__(self, state):
+ self.baz = state
+
+ class MyInt(int):
+ def __eq__(self, other):
+ return type(self) is type(other) and int(self) == int(other)
+
+ class MyList(list):
+ def __init__(self, n=1):
+ self.extend([None] * n)
+
+ def __eq__(self, other):
+ return type(self) is type(other) and list(self) == list(other)
+
+ class MyDict(dict):
+ def __init__(self, n=1):
+ for k in range(n):
+ self[k] = None
+
+ def __eq__(self, other):
+ return type(self) is type(other) and dict(self) == dict(other)
+
+ class FixedOffset(datetime.tzinfo):
+ def __init__(self, offset, name):
+ self.__offset = datetime.timedelta(minutes=offset)
+ self.__name = name
+
+ def utcoffset(self, dt):
+ return self.__offset
+
+ def tzname(self, dt):
+ return self.__name
+
+ def dst(self, dt):
+ return datetime.timedelta(0)
+
+ today = datetime.date.today()
+
+
+try:
+ from ruamel.ordereddict import ordereddict
+except ImportError:
+ from collections import OrderedDict
+
+ # to get the right name import ... as ordereddict doesn't do that
+
+ class ordereddict(OrderedDict):
+ pass
+
+
+def _load_code(expression):
+ return eval(expression, globals())
+
+
+def _serialize_value(data):
+ if isinstance(data, list):
+ return '[%s]' % ', '.join(map(_serialize_value, data))
+ elif isinstance(data, dict):
+ items = []
+ for key, value in data.items():
+ key = _serialize_value(key)
+ value = _serialize_value(value)
+ items.append('%s: %s' % (key, value))
+ items.sort()
+ return '{%s}' % ', '.join(items)
+ elif isinstance(data, datetime.datetime):
+ return repr(data.utctimetuple())
+ elif isinstance(data, float) and data != data:
+ return '?'
+ else:
+ return str(data)
+
+
+def test_constructor_types(data_filename, code_filename, verbose=False):
+ _make_objects()
+ native1 = None
+ native2 = None
+ yaml = ruamel.yaml.YAML(typ='safe', pure=True)
+ yaml.loader = MyLoader
+ try:
+ with open(data_filename, 'rb') as fp0:
+ native1 = list(yaml.load_all(fp0))
+ if len(native1) == 1:
+ native1 = native1[0]
+ with open(code_filename, 'rb') as fp0:
+ native2 = _load_code(fp0.read())
+ try:
+ if native1 == native2:
+ return
+ except TypeError:
+ pass
+ # print('native1', native1)
+ if verbose:
+ print('SERIALIZED NATIVE1:')
+ print(_serialize_value(native1))
+ print('SERIALIZED NATIVE2:')
+ print(_serialize_value(native2))
+ assert _serialize_value(native1) == _serialize_value(native2), (native1, native2)
+ finally:
+ if verbose:
+ print('NATIVE1:')
+ pprint.pprint(native1)
+ print('NATIVE2:')
+ pprint.pprint(native2)
+
+
+test_constructor_types.unittest = ['.data', '.code']
+
+
+def test_roundtrip_data(code_filename, roundtrip_filename, verbose=False):
+ _make_objects()
+ with open(code_filename, 'rb') as fp0:
+ value1 = fp0.read()
+ yaml = YAML(typ='safe', pure=True)
+ yaml.Loader = MyLoader
+ native2 = list(yaml.load_all(value1))
+ if len(native2) == 1:
+ native2 = native2[0]
+ try:
+ value2 = ruamel.yaml.dump(
+ native2,
+ Dumper=MyDumper,
+ default_flow_style=False,
+ allow_unicode=True,
+ encoding='utf-8',
+ )
+ # value2 += x
+ if verbose:
+ print('SERIALIZED NATIVE1:')
+ print(value1)
+ print('SERIALIZED NATIVE2:')
+ print(value2)
+ assert value1 == value2, (value1, value2)
+ finally:
+ if verbose:
+ print('NATIVE2:')
+ pprint.pprint(native2)
+
+
+test_roundtrip_data.unittest = ['.data', '.roundtrip']
+
+
+if __name__ == '__main__':
+ import sys
+ import test_constructor # NOQA
+
+ sys.modules['test_constructor'] = sys.modules['__main__']
+ import test_appliance
+
+ test_appliance.run(globals())
diff --git a/_test/lib/test_emitter.py b/_test/lib/test_emitter.py
new file mode 100644
index 0000000..b1991e3
--- /dev/null
+++ b/_test/lib/test_emitter.py
@@ -0,0 +1,132 @@
+from __future__ import absolute_import
+from __future__ import print_function
+
+import ruamel.yaml
+from ruamel.yaml import YAML
+
+
+def _compare_events(events1, events2):
+ assert len(events1) == len(events2), (events1, events2)
+ for event1, event2 in zip(events1, events2):
+ assert event1.__class__ == event2.__class__, (event1, event2)
+ if isinstance(event1, yaml.NodeEvent):
+ assert event1.anchor == event2.anchor, (event1, event2)
+ if isinstance(event1, yaml.CollectionStartEvent):
+ assert event1.tag == event2.tag, (event1, event2)
+ if isinstance(event1, yaml.ScalarEvent):
+ if True not in event1.implicit + event2.implicit:
+ assert event1.tag == event2.tag, (event1, event2)
+ assert event1.value == event2.value, (event1, event2)
+
+
+def test_emitter_on_data(data_filename, canonical_filename, verbose=False):
+ with open(data_filename, 'rb') as fp0:
+ events = list(YAML().parse(fp0))
+ output = YAML().emit(events)
+ if verbose:
+ print('OUTPUT:')
+ print(output)
+ new_events = list(yaml.parse(output))
+ _compare_events(events, new_events)
+
+
+test_emitter_on_data.unittest = ['.data', '.canonical']
+
+
+def test_emitter_on_canonical(canonical_filename, verbose=False):
+ with open(canonical_filename, 'rb') as fp0:
+ events = list(YAML().parse(fp0))
+ for canonical in [False, True]:
+ output = YAML().emit(events, canonical=canonical)
+ if verbose:
+ print('OUTPUT (canonical=%s):' % canonical)
+ print(output)
+ new_events = list(yaml.parse(output))
+ _compare_events(events, new_events)
+
+
+test_emitter_on_canonical.unittest = ['.canonical']
+
+
+def test_emitter_styles(data_filename, canonical_filename, verbose=False):
+ for filename in [data_filename, canonical_filename]:
+ with open(filename, 'rb') as fp0:
+ events = list(YAML().parse(fp0))
+ for flow_style in [False, True]:
+ for style in ['|', '>', '"', "'", ""]:
+ styled_events = []
+ for event in events:
+ if isinstance(event, yaml.ScalarEvent):
+ event = yaml.ScalarEvent(
+ event.anchor, event.tag, event.implicit, event.value, style=style
+ )
+ elif isinstance(event, yaml.SequenceStartEvent):
+ event = yaml.SequenceStartEvent(
+ event.anchor, event.tag, event.implicit, flow_style=flow_style
+ )
+ elif isinstance(event, yaml.MappingStartEvent):
+ event = yaml.MappingStartEvent(
+ event.anchor, event.tag, event.implicit, flow_style=flow_style
+ )
+ styled_events.append(event)
+ output = YAML().emit(styled_events)
+ if verbose:
+ print(
+ 'OUTPUT (filename=%r, flow_style=%r, style=%r)'
+ % (filename, flow_style, style)
+ )
+ print(output)
+ new_events = list(YAML().parse(output))
+ _compare_events(events, new_events)
+
+
+test_emitter_styles.unittest = ['.data', '.canonical']
+
+
+class EventsLoader(ruamel.yaml.Loader):
+ def construct_event(self, node):
+ if isinstance(node, ruamel.yaml.ScalarNode):
+ mapping = {}
+ else:
+ mapping = self.construct_mapping(node)
+ class_name = str(node.tag[1:]) + 'Event'
+ if class_name in [
+ 'AliasEvent',
+ 'ScalarEvent',
+ 'SequenceStartEvent',
+ 'MappingStartEvent',
+ ]:
+ mapping.setdefault('anchor', None)
+ if class_name in ['ScalarEvent', 'SequenceStartEvent', 'MappingStartEvent']:
+ mapping.setdefault('tag', None)
+ if class_name in ['SequenceStartEvent', 'MappingStartEvent']:
+ mapping.setdefault('implicit', True)
+ if class_name == 'ScalarEvent':
+ mapping.setdefault('implicit', (False, True))
+ mapping.setdefault('value', "")
+ value = getattr(yaml, class_name)(**mapping)
+ return value
+
+
+# if Loader is not a composite, add this function
+# EventsLoader.add_constructor = yaml.constructor.Constructor.add_constructor
+
+
+EventsLoader.add_constructor(None, EventsLoader.construct_event)
+
+
+def test_emitter_events(events_filename, verbose=False):
+ with open(events_filename, 'rb') as fp0:
+ events = list(YAML().load(fp0, Loader=EventsLoader))
+ output = YAML().emit(events)
+ if verbose:
+ print('OUTPUT:')
+ print(output)
+ new_events = list(YAML().parse(output))
+ _compare_events(events, new_events)
+
+
+if __name__ == '__main__':
+ import test_appliance
+
+ test_appliance.run(globals())
diff --git a/_test/lib/test_errors.py b/_test/lib/test_errors.py
new file mode 100644
index 0000000..c0fd3df
--- /dev/null
+++ b/_test/lib/test_errors.py
@@ -0,0 +1,92 @@
+
+import ruamel.yaml
+YAML = ruamel.yaml.YAML
+
+import test_emitter
+import warnings
+
+warnings.simplefilter('ignore', ruamel.yaml.error.UnsafeLoaderWarning)
+
+
+def test_loader_error(error_filename, verbose=False):
+ yaml = YAML(typ='safe', pure=True)
+ try:
+ with open(error_filename, 'rb') as fp0:
+ list(yaml.load_all(fp0))
+ except yaml.YAMLError as exc:
+ if verbose:
+ print('%s:' % exc.__class__.__name__, exc)
+ else:
+ raise AssertionError('expected an exception')
+
+
+test_loader_error.unittest = ['.loader-error']
+
+
+def test_loader_error_string(error_filename, verbose=False):
+ yaml = YAML(typ='safe', pure=True)
+ try:
+ with open(error_filename, 'rb') as fp0:
+ list(yaml.load_all(fp0.read()))
+ except yaml.YAMLError as exc:
+ if verbose:
+ print('%s:' % exc.__class__.__name__, exc)
+ else:
+ raise AssertionError('expected an exception')
+
+
+test_loader_error_string.unittest = ['.loader-error']
+
+
+def test_loader_error_single(error_filename, verbose=False):
+ yaml = YAML(typ='safe', pure=True)
+ try:
+ with open(error_filename, 'rb') as fp0:
+ yaml.load(fp0.read())
+ except yaml.YAMLError as exc:
+ if verbose:
+ print('%s:' % exc.__class__.__name__, exc)
+ else:
+ raise AssertionError('expected an exception')
+
+
+test_loader_error_single.unittest = ['.single-loader-error']
+
+
+def test_emitter_error(error_filename, verbose=False):
+ yaml = YAML(typ='safe', pure=True)
+ with open(error_filename, 'rb') as fp0:
+ events = list(yaml.load(fp0, Loader=test_emitter.EventsLoader))
+ try:
+ ruamel.yaml.emit(events)
+ except yaml.YAMLError as exc:
+ if verbose:
+ print('%s:' % exc.__class__.__name__, exc)
+ else:
+ raise AssertionError('expected an exception')
+
+
+test_emitter_error.unittest = ['.emitter-error']
+
+
+def test_dumper_error(error_filename, verbose=False):
+ yaml = YAML(typ='safe', pure=True)
+ with open(error_filename, 'rb') as fp0:
+ code = fp0.read()
+ try:
+ import yaml
+
+ exec(code)
+ except yaml.YAMLError as exc:
+ if verbose:
+ print('%s:' % exc.__class__.__name__, exc)
+ else:
+ raise AssertionError('expected an exception')
+
+
+test_dumper_error.unittest = ['.dumper-error']
+
+if __name__ == '__main__':
+ import test_appliance
+
+ test_appliance.run(globals())
diff --git a/_test/lib/test_input_output.py b/_test/lib/test_input_output.py
new file mode 100644
index 0000000..37bda3d
--- /dev/null
+++ b/_test/lib/test_input_output.py
@@ -0,0 +1,180 @@
+
+from ruamel.yaml import YAML
+import codecs
+import tempfile
+import os
+import os.path
+from ruamel.yaml.compat import StringIO, BytesIO
+
+def test_unicode_input(unicode_filename, verbose=False):
+ yaml = YAML(typ='safe', pure=True)
+ with open(unicode_filename, 'rb') as fp:
+ data = fp.read().decode('utf-8')
+ value = ' '.join(data.split())
+ output = yaml.load(data)
+ assert output == value, (output, value)
+ output = yaml.load(StringIO(data))
+ assert output == value, (output, value)
+ for input in [
+ data.encode('utf-8'),
+ codecs.BOM_UTF8 + data.encode('utf-8'),
+ codecs.BOM_UTF16_BE + data.encode('utf-16-be'),
+ codecs.BOM_UTF16_LE + data.encode('utf-16-le'),
+ ]:
+ if verbose:
+ print('INPUT:', repr(input[:10]), '...')
+ output = yaml.load(input)
+ assert output == value, (output, value)
+ output = yaml.load(BytesIO(input))
+ assert output == value, (output, value)
+
+
+test_unicode_input.unittest = ['.unicode']
+
+
+def test_unicode_input_errors(unicode_filename, verbose=False):
+ yaml = YAML(typ='safe', pure=True)
+ with open(unicode_filename, 'rb') as fp:
+ data = fp.read().decode('utf-8')
+ for input in [
+ data.encode('latin1', 'ignore'),
+ data.encode('utf-16-be'),
+ data.encode('utf-16-le'),
+ codecs.BOM_UTF8 + data.encode('utf-16-be'),
+ codecs.BOM_UTF16_BE + data.encode('utf-16-le'),
+ codecs.BOM_UTF16_LE + data.encode('utf-8') + b'!',
+ ]:
+ try:
+ yaml.load(input)
+ except yaml.YAMLError as exc:
+ if verbose:
+ print(exc)
+ else:
+ raise AssertionError('expected an exception')
+ try:
+ yaml.load(BytesIO(input))
+ except yaml.YAMLError as exc:
+ if verbose:
+ print(exc)
+ else:
+ raise AssertionError('expected an exception')
+
+
+test_unicode_input_errors.unittest = ['.unicode']
+
+
+def test_unicode_output(unicode_filename, verbose=False):
+ yaml = YAML(typ='safe', pure=True)
+ with open(unicode_filename, 'rb') as fp:
+ data = fp.read().decode('utf-8')
+ value = ' '.join(data.split())
+ for allow_unicode in [False, True]:
+ data1 = yaml.dump(value, allow_unicode=allow_unicode)
+ for encoding in [None, 'utf-8', 'utf-16-be', 'utf-16-le']:
+ stream = StringIO()
+ yaml.dump(value, stream, encoding=encoding, allow_unicode=allow_unicode)
+ data2 = stream.getvalue()
+ data3 = yaml.dump(value, encoding=encoding, allow_unicode=allow_unicode)
+ if encoding is not None:
+ assert isinstance(data3, bytes)
+ data3 = data3.decode(encoding)
+ stream = BytesIO()
+ if encoding is None:
+ try:
+ yaml.dump(
+ value, stream, encoding=encoding, allow_unicode=allow_unicode
+ )
+ except TypeError as exc:
+ if verbose:
+ print(exc)
+ data4 = None
+ else:
+ raise AssertionError('expected an exception')
+ else:
+ yaml.dump(value, stream, encoding=encoding, allow_unicode=allow_unicode)
+ data4 = stream.getvalue()
+ if verbose:
+ print('BYTES:', data4[:50])
+ data4 = data4.decode(encoding)
+ for copy in [data1, data2, data3, data4]:
+ if copy is None:
+ continue
+ assert isinstance(copy, str)
+ if allow_unicode:
+ try:
+ copy[4:].encode('ascii')
+ except UnicodeEncodeError as exc:
+ if verbose:
+ print(exc)
+ else:
+ raise AssertionError('expected an exception')
+ else:
+ copy[4:].encode('ascii')
+ assert isinstance(data1, str), (type(data1), encoding)
+ assert isinstance(data2, str), (type(data2), encoding)
+
+
+test_unicode_output.unittest = ['.unicode']
+
+
+def test_file_output(unicode_filename, verbose=False):
+ yaml = YAML(typ='safe', pure=True)
+ with open(unicode_filename, 'rb') as fp:
+ data = fp.read().decode('utf-8')
+ handle, filename = tempfile.mkstemp()
+ os.close(handle)
+ try:
+ stream = StringIO()
+ yaml.dump(data, stream, allow_unicode=True)
+ data1 = stream.getvalue()
+ stream = BytesIO()
+ yaml.dump(data, stream, encoding='utf-16-le', allow_unicode=True)
+ data2 = stream.getvalue().decode('utf-16-le')[1:]
+ with open(filename, 'w', encoding='utf-16-le') as stream:
+ yaml.dump(data, stream, allow_unicode=True)
+ with open(filename, 'r', encoding='utf-16-le') as fp0:
+ data3 = fp0.read()
+ with open(filename, 'wb') as stream:
+ yaml.dump(data, stream, encoding='utf-8', allow_unicode=True)
+ with open(filename, 'r', encoding='utf-8') as fp0:
+ data4 = fp0.read()
+ assert data1 == data2, (data1, data2)
+ assert data1 == data3, (data1, data3)
+ assert data1 == data4, (data1, data4)
+ finally:
+ if os.path.exists(filename):
+ os.unlink(filename)
+
+
+test_file_output.unittest = ['.unicode']
+
+
+def test_unicode_transfer(unicode_filename, verbose=False):
+ yaml = YAML(typ='safe', pure=True)
+ with open(unicode_filename, 'rb') as fp:
+ data = fp.read().decode('utf-8')
+ for encoding in [None, 'utf-8', 'utf-16-be', 'utf-16-le']:
+ input = data
+ if encoding is not None:
+ input = ('\ufeff' + input).encode(encoding)
+ output1 = yaml.emit(yaml.parse(input), allow_unicode=True)
+ if encoding is None:
+ stream = StringIO()
+ else:
+ stream = BytesIO()
+ yaml.emit(yaml.parse(input), stream, allow_unicode=True)
+ output2 = stream.getvalue()
+ assert isinstance(output1, str), (type(output1), encoding)
+ if encoding is None:
+ assert isinstance(output2, str), (type(output1), encoding)
+ else:
+ assert isinstance(output2, bytes), (type(output1), encoding)
+ output2.decode(encoding)
+
+
+test_unicode_transfer.unittest = ['.unicode']
+
+if __name__ == '__main__':
+ import test_appliance
+
+ test_appliance.run(globals())
diff --git a/_test/lib/test_mark.py b/_test/lib/test_mark.py
new file mode 100644
index 0000000..2644a79
--- /dev/null
+++ b/_test/lib/test_mark.py
@@ -0,0 +1,35 @@
+
+import ruamel.yaml as yaml
+
+
+def test_marks(marks_filename, verbose=False):
+ with open(marks_filename, 'r') as fp0:
+ inputs = fp0.read().split('---\n')[1:]
+ for input in inputs:
+ index = 0
+ line = 0
+ column = 0
+ while input[index] != '*':
+ if input[index] == '\n':
+ line += 1
+ column = 0
+ else:
+ column += 1
+ index += 1
+ mark = yaml.Mark(marks_filename, index, line, column, str(input), index)
+ snippet = mark.get_snippet(indent=2, max_length=79)
+ if verbose:
+ print(snippet)
+ assert isinstance(snippet, str), type(snippet)
+ assert snippet.count('\n') == 1, snippet.count('\n')
+ data, pointer = snippet.split('\n')
+ assert len(data) < 82, len(data)
+ assert data[len(pointer) - 1] == '*', data[len(pointer) - 1]
+
+
+test_marks.unittest = ['.marks']
+
+if __name__ == '__main__':
+ import test_appliance
+
+ test_appliance.run(globals())
diff --git a/_test/lib/test_reader.py b/_test/lib/test_reader.py
new file mode 100644
index 0000000..16b9cd7
--- /dev/null
+++ b/_test/lib/test_reader.py
@@ -0,0 +1,44 @@
+
+import codecs # NOQA
+import io
+
+import ruamel.yaml.reader
+
+
+def _run_reader(data, verbose):
+ try:
+ stream = ruamel.yaml.py.reader.Reader(data)
+ while stream.peek() != '\0':
+ stream.forward()
+ except ruamel.yaml.py.reader.ReaderError as exc:
+ if verbose:
+ print(exc)
+ else:
+ raise AssertionError('expected an exception')
+
+
+def test_stream_error(error_filename, verbose=False):
+ with open(error_filename, 'rb') as fp0:
+ _run_reader(fp0, verbose)
+ with open(error_filename, 'rb') as fp0:
+ _run_reader(fp0.read(), verbose)
+ for encoding in ['utf-8', 'utf-16-le', 'utf-16-be']:
+ try:
+ with open(error_filename, 'rb') as fp0:
+ data = fp0.read().decode(encoding)
+ break
+ except UnicodeDecodeError:
+ pass
+ else:
+ return
+ _run_reader(data, verbose)
+ with io.open(error_filename, encoding=encoding) as fp:
+ _run_reader(fp, verbose)
+
+
+test_stream_error.unittest = ['.stream-error']
+
+if __name__ == '__main__':
+ import test_appliance
+
+ test_appliance.run(globals())
diff --git a/_test/lib/test_recursive.py b/_test/lib/test_recursive.py
new file mode 100644
index 0000000..88858e4
--- /dev/null
+++ b/_test/lib/test_recursive.py
@@ -0,0 +1,58 @@
+
+import ruamel.yaml
+
+
+class AnInstance:
+ def __init__(self, foo, bar):
+ self.foo = foo
+ self.bar = bar
+
+ def __repr__(self):
+ try:
+ return '%s(foo=%r, bar=%r)' % (self.__class__.__name__, self.foo, self.bar)
+ except RuntimeError:
+ return '%s(foo=..., bar=...)' % self.__class__.__name__
+
+
+class AnInstanceWithState(AnInstance):
+ def __getstate__(self):
+ return {'attributes': [self.foo, self.bar]}
+
+ def __setstate__(self, state):
+ self.foo, self.bar = state['attributes']
+
+
+def test_recursive(recursive_filename, verbose=False):
+ yaml = ruamel.yaml.YAML(typ='safe', pure=True)
+ context = globals().copy()
+ with open(recursive_filename, 'rb') as fp0:
+ exec(fp0.read(), context)
+ value1 = context['value']
+ output1 = None
+ value2 = None
+ output2 = None
+ try:
+ buf = ruamel.yaml.compat.StringIO()
+ output1 = yaml.dump(value1, buf)
+ yaml.load(output1)
+ value2 = buf.getvalue()
+ buf = ruamel.yaml.compat.StringIO()
+ yaml.dump(value2, buf)
+ output2 = buf.getvalue()
+ assert output1 == output2, (output1, output2)
+ finally:
+ if verbose:
+ print('VALUE1:', value1)
+ print('VALUE2:', value2)
+ print('OUTPUT1:')
+ print(output1)
+ print('OUTPUT2:')
+ print(output2)
+
+
+test_recursive.unittest = ['.recursive']
+
+if __name__ == '__main__':
+ import test_appliance
+
+ test_appliance.run(globals())
diff --git a/_test/lib/test_representer.py b/_test/lib/test_representer.py
new file mode 100644
index 0000000..5b2415d
--- /dev/null
+++ b/_test/lib/test_representer.py
@@ -0,0 +1,51 @@
+
+from ruamel.yaml import YAML
+import test_constructor
+import pprint
+
+
+def test_representer_types(code_filename, verbose=False):
+ yaml = YAML(typ='safe', pure=True)
+ test_constructor._make_objects()
+ for allow_unicode in [False, True]:
+ for encoding in ['utf-8', 'utf-16-be', 'utf-16-le']:
+ with open(code_filename, 'rb') as fp0:
+ native1 = test_constructor._load_code(fp0.read())
+ native2 = None
+ try:
+ output = yaml.dump(
+ native1,
+ Dumper=test_constructor.MyDumper,
+ allow_unicode=allow_unicode,
+ encoding=encoding,
+ )
+ native2 = yaml.load(output, Loader=test_constructor.MyLoader)
+ try:
+ if native1 == native2:
+ continue
+ except TypeError:
+ pass
+ value1 = test_constructor._serialize_value(native1)
+ value2 = test_constructor._serialize_value(native2)
+ if verbose:
+ print('SERIALIZED NATIVE1:')
+ print(value1)
+ print('SERIALIZED NATIVE2:')
+ print(value2)
+ assert value1 == value2, (native1, native2)
+ finally:
+ if verbose:
+ print('NATIVE1:')
+ pprint.pprint(native1)
+ print('NATIVE2:')
+ pprint.pprint(native2)
+ print('OUTPUT:')
+ print(output)
+
+
+test_representer_types.unittest = ['.code']
+
+if __name__ == '__main__':
+ import test_appliance
+
+ test_appliance.run(globals())
diff --git a/_test/lib/test_resolver.py b/_test/lib/test_resolver.py
new file mode 100644
index 0000000..b2b0839
--- /dev/null
+++ b/_test/lib/test_resolver.py
@@ -0,0 +1,111 @@
+
+import ruamel.yaml
+yaml = ruamel.yaml.YAML()
+import pprint
+
+
+def test_implicit_resolver(data_filename, detect_filename, verbose=False):
+ correct_tag = None
+ node = None
+ try:
+ with open(detect_filename, 'r') as fp0:
+ correct_tag = fp0.read().strip()
+ with open(data_filename, 'rb') as fp0:
+ node = yaml.compose(fp0)
+ assert isinstance(node, yaml.SequenceNode), node
+ for scalar in node.value:
+ assert isinstance(scalar, yaml.ScalarNode), scalar
+ assert scalar.tag == correct_tag, (scalar.tag, correct_tag)
+ finally:
+ if verbose:
+ print('CORRECT TAG:', correct_tag)
+ if hasattr(node, 'value'):
+ print('CHILDREN:')
+ pprint.pprint(node.value)
+
+
+test_implicit_resolver.unittest = ['.data', '.detect']
+
+
+def _make_path_loader_and_dumper():
+ global MyLoader, MyDumper
+
+ class MyLoader(yaml.Loader):
+ pass
+
+ class MyDumper(yaml.Dumper):
+ pass
+
+ yaml.add_path_resolver('!root', [], Loader=MyLoader, Dumper=MyDumper)
+ yaml.add_path_resolver('!root/scalar', [], str, Loader=MyLoader, Dumper=MyDumper)
+ yaml.add_path_resolver(
+ '!root/key11/key12/*', ['key11', 'key12'], Loader=MyLoader, Dumper=MyDumper
+ )
+ yaml.add_path_resolver('!root/key21/1/*', ['key21', 1], Loader=MyLoader, Dumper=MyDumper)
+ yaml.add_path_resolver(
+ '!root/key31/*/*/key14/map',
+ ['key31', None, None, 'key14'],
+ dict,
+ Loader=MyLoader,
+ Dumper=MyDumper,
+ )
+
+ return MyLoader, MyDumper
+
+
+def _convert_node(node):
+ if isinstance(node, yaml.ScalarNode):
+ return (node.tag, node.value)
+ elif isinstance(node, yaml.SequenceNode):
+ value = []
+ for item in node.value:
+ value.append(_convert_node(item))
+ return (node.tag, value)
+ elif isinstance(node, yaml.MappingNode):
+ value = []
+ for key, item in node.value:
+ value.append((_convert_node(key), _convert_node(item)))
+ return (node.tag, value)
+
+
+def test_path_resolver_loader(data_filename, path_filename, verbose=False):
+ _make_path_loader_and_dumper()
+ with open(data_filename, 'rb') as fp0:
+ nodes1 = list(yaml.compose_all(fp0.read(), Loader=MyLoader))
+ with open(path_filename, 'rb') as fp0:
+ nodes2 = list(yaml.compose_all(fp0.read()))
+ try:
+ for node1, node2 in zip(nodes1, nodes2):
+ data1 = _convert_node(node1)
+ data2 = _convert_node(node2)
+ assert data1 == data2, (data1, data2)
+ finally:
+ if verbose:
+ print(yaml.serialize_all(nodes1))
+
+
+test_path_resolver_loader.unittest = ['.data', '.path']
+
+
+def test_path_resolver_dumper(data_filename, path_filename, verbose=False):
+ _make_path_loader_and_dumper()
+ for filename in [data_filename, path_filename]:
+ with open(filename, 'rb') as fp0:
+ output = yaml.serialize_all(yaml.compose_all(fp0), Dumper=MyDumper)
+ if verbose:
+ print(output)
+ nodes1 = yaml.compose_all(output)
+ with open(data_filename, 'rb') as fp0:
+ nodes2 = yaml.compose_all(fp0)
+ for node1, node2 in zip(nodes1, nodes2):
+ data1 = _convert_node(node1)
+ data2 = _convert_node(node2)
+ assert data1 == data2, (data1, data2)
+
+
+test_path_resolver_dumper.unittest = ['.data', '.path']
+
+if __name__ == '__main__':
+ import test_appliance
+
+ test_appliance.run(globals())
diff --git a/_test/lib/test_structure.py b/_test/lib/test_structure.py
new file mode 100644
index 0000000..8de24a3
--- /dev/null
+++ b/_test/lib/test_structure.py
@@ -0,0 +1,225 @@
+
+import ruamel.yaml
+import canonical # NOQA
+import pprint
+
+
+def _convert_structure(loader):
+ if loader.check_event(ruamel.yaml.ScalarEvent):
+ event = loader.get_event()
+ if event.tag or event.anchor or event.value:
+ return True
+ else:
+ return None
+ elif loader.check_event(ruamel.yaml.SequenceStartEvent):
+ loader.get_event()
+ sequence = []
+ while not loader.check_event(ruamel.yaml.SequenceEndEvent):
+ sequence.append(_convert_structure(loader))
+ loader.get_event()
+ return sequence
+ elif loader.check_event(ruamel.yaml.MappingStartEvent):
+ loader.get_event()
+ mapping = []
+ while not loader.check_event(ruamel.yaml.MappingEndEvent):
+ key = _convert_structure(loader)
+ value = _convert_structure(loader)
+ mapping.append((key, value))
+ loader.get_event()
+ return mapping
+ elif loader.check_event(ruamel.yaml.AliasEvent):
+ loader.get_event()
+ return '*'
+ else:
+ loader.get_event()
+ return '?'
+
+
+def test_structure(data_filename, structure_filename, verbose=False):
+ nodes1 = []
+ with open(structure_filename, 'r') as fp:
+ nodes2 = eval(fp.read())
+ try:
+ with open(data_filename, 'rb') as fp:
+ loader = ruamel.yaml.Loader(fp)
+ while loader.check_event():
+ if loader.check_event(
+ ruamel.yaml.StreamStartEvent,
+ ruamel.yaml.StreamEndEvent,
+ ruamel.yaml.DocumentStartEvent,
+ ruamel.yaml.DocumentEndEvent,
+ ):
+ loader.get_event()
+ continue
+ nodes1.append(_convert_structure(loader))
+ if len(nodes1) == 1:
+ nodes1 = nodes1[0]
+ assert nodes1 == nodes2, (nodes1, nodes2)
+ finally:
+ if verbose:
+ print('NODES1:')
+ pprint.pprint(nodes1)
+ print('NODES2:')
+ pprint.pprint(nodes2)
+
+
+test_structure.unittest = ['.data', '.structure']
+
+
+def _compare_events(events1, events2, full=False):
+ assert len(events1) == len(events2), (len(events1), len(events2))
+ for event1, event2 in zip(events1, events2):
+ assert event1.__class__ == event2.__class__, (event1, event2)
+ if isinstance(event1, ruamel.yaml.AliasEvent) and full:
+ assert event1.anchor == event2.anchor, (event1, event2)
+ if isinstance(event1, (ruamel.yaml.ScalarEvent, ruamel.yaml.CollectionStartEvent)):
+ if (event1.tag not in [None, '!'] and event2.tag not in [None, '!']) or full:
+ assert event1.tag == event2.tag, (event1, event2)
+ if isinstance(event1, ruamel.yaml.ScalarEvent):
+ assert event1.value == event2.value, (event1, event2)
+
+
+def test_parser(data_filename, canonical_filename, verbose=False):
+ events1 = None
+ events2 = None
+ try:
+ with open(data_filename, 'rb') as fp0:
+ events1 = list(ruamel.yaml.YAML().parse(fp0))
+ with open(canonical_filename, 'rb') as fp0:
+ events2 = list(ruamel.yaml.YAML().canonical_parse(fp0))
+ _compare_events(events1, events2)
+ finally:
+ if verbose:
+ print('EVENTS1:')
+ pprint.pprint(events1)
+ print('EVENTS2:')
+ pprint.pprint(events2)
+
+
+test_parser.unittest = ['.data', '.canonical']
+
+
+def test_parser_on_canonical(canonical_filename, verbose=False):
+ events1 = None
+ events2 = None
+ try:
+ with open(canonical_filename, 'rb') as fp0:
+ events1 = list(ruamel.yaml.YAML().parse(fp0))
+ with open(canonical_filename, 'rb') as fp0:
+ events2 = list(ruamel.yaml.YAML().canonical_parse(fp0))
+ _compare_events(events1, events2, full=True)
+ finally:
+ if verbose:
+ print('EVENTS1:')
+ pprint.pprint(events1)
+ print('EVENTS2:')
+ pprint.pprint(events2)
+
+
+test_parser_on_canonical.unittest = ['.canonical']
+
+
+def _compare_nodes(node1, node2):
+ assert node1.__class__ == node2.__class__, (node1, node2)
+ assert node1.tag == node2.tag, (node1, node2)
+ if isinstance(node1, ruamel.yaml.ScalarNode):
+ assert node1.value == node2.value, (node1, node2)
+ else:
+ assert len(node1.value) == len(node2.value), (node1, node2)
+ for item1, item2 in zip(node1.value, node2.value):
+ if not isinstance(item1, tuple):
+ item1 = (item1,)
+ item2 = (item2,)
+ for subnode1, subnode2 in zip(item1, item2):
+ _compare_nodes(subnode1, subnode2)
+
+
+def test_composer(data_filename, canonical_filename, verbose=False):
+ nodes1 = None
+ nodes2 = None
+ try:
+ yaml = ruamel.yaml.YAML()
+ with open(data_filename, 'rb') as fp0:
+ nodes1 = list(yaml.compose_all(fp0))
+ with open(canonical_filename, 'rb') as fp0:
+ nodes2 = list(yaml.canonical_compose_all(fp0))
+ assert len(nodes1) == len(nodes2), (len(nodes1), len(nodes2))
+ for node1, node2 in zip(nodes1, nodes2):
+ _compare_nodes(node1, node2)
+ finally:
+ if verbose:
+ print('NODES1:')
+ pprint.pprint(nodes1)
+ print('NODES2:')
+ pprint.pprint(nodes2)
+
+
+test_composer.unittest = ['.data', '.canonical']
+
+
+def _make_loader():
+ global MyLoader
+
+ class MyLoader(ruamel.yaml.Loader):
+ def construct_sequence(self, node):
+ return tuple(ruamel.yaml.Loader.construct_sequence(self, node))
+
+ def construct_mapping(self, node):
+ pairs = self.construct_pairs(node)
+ pairs.sort(key=(lambda i: str(i)))
+ return pairs
+
+ def construct_undefined(self, node):
+ return self.construct_scalar(node)
+
+ MyLoader.add_constructor('tag:yaml.org,2002:map', MyLoader.construct_mapping)
+ MyLoader.add_constructor(None, MyLoader.construct_undefined)
+
+
+def _make_canonical_loader():
+ global MyCanonicalLoader
+
+ class MyCanonicalLoader(ruamel.yaml.CanonicalLoader):
+ def construct_sequence(self, node):
+ return tuple(ruamel.yaml.CanonicalLoader.construct_sequence(self, node))
+
+ def construct_mapping(self, node):
+ pairs = self.construct_pairs(node)
+ pairs.sort(key=(lambda i: str(i)))
+ return pairs
+
+ def construct_undefined(self, node):
+ return self.construct_scalar(node)
+
+ MyCanonicalLoader.add_constructor(
+ 'tag:yaml.org,2002:map', MyCanonicalLoader.construct_mapping
+ )
+ MyCanonicalLoader.add_constructor(None, MyCanonicalLoader.construct_undefined)
+
+
+def test_constructor(data_filename, canonical_filename, verbose=False):
+ _make_loader()
+ _make_canonical_loader()
+ native1 = None
+ native2 = None
+ yaml = YAML(typ='safe')
+ try:
+ with open(data_filename, 'rb') as fp0:
+ native1 = list(yaml.load(fp0, Loader=MyLoader))
+ with open(canonical_filename, 'rb') as fp0:
+ native2 = list(yaml.load(fp0, Loader=MyCanonicalLoader))
+ assert native1 == native2, (native1, native2)
+ finally:
+ if verbose:
+ print('NATIVE1:')
+ pprint.pprint(native1)
+ print('NATIVE2:')
+ pprint.pprint(native2)
+
+
+test_constructor.unittest = ['.data', '.canonical']
+
+if __name__ == '__main__':
+ import test_appliance
+
+ test_appliance.run(globals())
diff --git a/_test/lib/test_tokens.py b/_test/lib/test_tokens.py
new file mode 100644
index 0000000..575e95c
--- /dev/null
+++ b/_test/lib/test_tokens.py
@@ -0,0 +1,86 @@
+
+import ruamel.yaml
+import pprint
+
+# Tokens mnemonic:
+# directive: %
+# document_start: ---
+# document_end: ...
+# alias: *
+# anchor: &
+# tag: !
+# scalar _
+# block_sequence_start: [[
+# block_mapping_start: {{
+# block_end: ]}
+# flow_sequence_start: [
+# flow_sequence_end: ]
+# flow_mapping_start: {
+# flow_mapping_end: }
+# entry: ,
+# key: ?
+# value: :
+
+_replaces = {
+ ruamel.yaml.DirectiveToken: '%',
+ ruamel.yaml.DocumentStartToken: '---',
+ ruamel.yaml.DocumentEndToken: '...',
+ ruamel.yaml.AliasToken: '*',
+ ruamel.yaml.AnchorToken: '&',
+ ruamel.yaml.TagToken: '!',
+ ruamel.yaml.ScalarToken: '_',
+ ruamel.yaml.BlockSequenceStartToken: '[[',
+ ruamel.yaml.BlockMappingStartToken: '{{',
+ ruamel.yaml.BlockEndToken: ']}',
+ ruamel.yaml.FlowSequenceStartToken: '[',
+ ruamel.yaml.FlowSequenceEndToken: ']',
+ ruamel.yaml.FlowMappingStartToken: '{',
+ ruamel.yaml.FlowMappingEndToken: '}',
+ ruamel.yaml.BlockEntryToken: ',',
+ ruamel.yaml.FlowEntryToken: ',',
+ ruamel.yaml.KeyToken: '?',
+ ruamel.yaml.ValueToken: ':',
+}
+
+
+def test_tokens(data_filename, tokens_filename, verbose=False):
+ tokens1 = []
+ with open(tokens_filename, 'r') as fp:
+ tokens2 = fp.read().split()
+ try:
+ yaml = ruamel.yaml.YAML(typ='unsafe', pure=True)
+ with open(data_filename, 'rb') as fp1:
+ for token in yaml.scan(fp1):
+ if not isinstance(token, (ruamel.yaml.StreamStartToken, ruamel.yaml.StreamEndToken)):
+ tokens1.append(_replaces[token.__class__])
+ finally:
+ if verbose:
+ print('TOKENS1:', ' '.join(tokens1))
+ print('TOKENS2:', ' '.join(tokens2))
+ assert len(tokens1) == len(tokens2), (tokens1, tokens2)
+ for token1, token2 in zip(tokens1, tokens2):
+ assert token1 == token2, (token1, token2)
+
+
+test_tokens.unittest = ['.data', '.tokens']
+
+
+def test_scanner(data_filename, canonical_filename, verbose=False):
+ for filename in [data_filename, canonical_filename]:
+ tokens = []
+ try:
+ yaml = ruamel.yaml.YAML(typ='unsafe', pure=False)
+ with open(filename, 'rb') as fp:
+ for token in yaml.scan(fp):
+ tokens.append(token.__class__.__name__)
+ finally:
+ if verbose:
+ pprint.pprint(tokens)
+
+
+test_scanner.unittest = ['.data', '.canonical']
+
+if __name__ == '__main__':
+ import test_appliance
+
+ test_appliance.run(globals())
diff --git a/_test/lib/test_yaml.py b/_test/lib/test_yaml.py
new file mode 100644
index 0000000..cf64a73
--- /dev/null
+++ b/_test/lib/test_yaml.py
@@ -0,0 +1,20 @@
+# coding: utf-8
+
+from test_mark import * # NOQA
+from test_reader import * # NOQA
+from test_canonical import * # NOQA
+from test_tokens import * # NOQA
+from test_structure import * # NOQA
+from test_errors import * # NOQA
+from test_resolver import * # NOQA
+from test_constructor import * # NOQA
+from test_emitter import * # NOQA
+from test_representer import * # NOQA
+from test_recursive import * # NOQA
+from test_input_output import * # NOQA
+
+if __name__ == '__main__':
+ import sys
+ import test_appliance
+
+ sys.exit(test_appliance.run(globals()))
diff --git a/_test/lib/test_yaml_ext.py b/_test/lib/test_yaml_ext.py
new file mode 100644
index 0000000..a6fa287
--- /dev/null
+++ b/_test/lib/test_yaml_ext.py
@@ -0,0 +1,403 @@
+# coding: utf-8
+
+import _ruamel_yaml
+import ruamel.yaml
+import types
+import pprint
+
+ruamel.yaml.PyBaseLoader = ruamel.yaml.BaseLoader
+ruamel.yaml.PySafeLoader = ruamel.yaml.SafeLoader
+ruamel.yaml.PyLoader = ruamel.yaml.Loader
+ruamel.yaml.PyBaseDumper = ruamel.yaml.BaseDumper
+ruamel.yaml.PySafeDumper = ruamel.yaml.SafeDumper
+ruamel.yaml.PyDumper = ruamel.yaml.Dumper
+
+old_scan = ruamel.yaml.scan
+
+
+def new_scan(stream, Loader=ruamel.yaml.CLoader):
+ return old_scan(stream, Loader)
+
+
+old_parse = ruamel.yaml.parse
+
+
+def new_parse(stream, Loader=ruamel.yaml.CLoader):
+ return old_parse(stream, Loader)
+
+
+old_compose = ruamel.yaml.compose
+
+
+def new_compose(stream, Loader=ruamel.yaml.CLoader):
+ return old_compose(stream, Loader)
+
+
+old_compose_all = ruamel.yaml.compose_all
+
+
+def new_compose_all(stream, Loader=ruamel.yaml.CLoader):
+ return old_compose_all(stream, Loader)
+
+
+old_load = ruamel.yaml.load
+
+
+def new_load(stream, Loader=ruamel.yaml.CLoader):
+ return old_load(stream, Loader)
+
+
+old_load_all = ruamel.yaml.load_all
+
+
+def new_load_all(stream, Loader=ruamel.yaml.CLoader):
+ return old_load_all(stream, Loader)
+
+
+old_safe_load = ruamel.yaml.safe_load
+
+
+def new_safe_load(stream):
+ return old_load(stream, ruamel.yaml.CSafeLoader)
+
+
+old_safe_load_all = ruamel.yaml.safe_load_all
+
+
+def new_safe_load_all(stream):
+ return old_load_all(stream, ruamel.yaml.CSafeLoader)
+
+
+old_emit = ruamel.yaml.emit
+
+
+def new_emit(events, stream=None, Dumper=ruamel.yaml.CDumper, **kwds):
+ return old_emit(events, stream, Dumper, **kwds)
+
+
+old_serialize = ruamel.yaml.serialize
+
+
+def new_serialize(node, stream, Dumper=ruamel.yaml.CDumper, **kwds):
+ return old_serialize(node, stream, Dumper, **kwds)
+
+
+old_serialize_all = ruamel.yaml.serialize_all
+
+
+def new_serialize_all(nodes, stream=None, Dumper=ruamel.yaml.CDumper, **kwds):
+ return old_serialize_all(nodes, stream, Dumper, **kwds)
+
+
+old_dump = ruamel.yaml.dump
+
+
+def new_dump(data, stream=None, Dumper=ruamel.yaml.CDumper, **kwds):
+ return old_dump(data, stream, Dumper, **kwds)
+
+
+old_dump_all = ruamel.yaml.dump_all
+
+
+def new_dump_all(documents, stream=None, Dumper=ruamel.yaml.CDumper, **kwds):
+ return old_dump_all(documents, stream, Dumper, **kwds)
+
+
+old_safe_dump = ruamel.yaml.safe_dump
+
+
+def new_safe_dump(data, stream=None, **kwds):
+ return old_dump(data, stream, ruamel.yaml.CSafeDumper, **kwds)
+
+
+# old_safe_dump_all = ruamel.yaml.safe_dump_all
+
+
+def new_safe_dump_all(documents, stream=None, **kwds):
+ return old_dump_all(documents, stream, ruamel.yaml.CSafeDumper, **kwds)
+
+
+def _set_up():
+ ruamel.yaml.BaseLoader = ruamel.yaml.CBaseLoader
+ ruamel.yaml.SafeLoader = ruamel.yaml.CSafeLoader
+ ruamel.yaml.Loader = ruamel.yaml.CLoader
+ ruamel.yaml.BaseDumper = ruamel.yaml.CBaseDumper
+ ruamel.yaml.SafeDumper = ruamel.yaml.CSafeDumper
+ ruamel.yaml.Dumper = ruamel.yaml.CDumper
+ ruamel.yaml.scan = new_scan
+ ruamel.yaml.parse = new_parse
+ ruamel.yaml.compose = new_compose
+ ruamel.yaml.compose_all = new_compose_all
+ ruamel.yaml.load = new_load
+ ruamel.yaml.load_all = new_load_all
+ ruamel.yaml.safe_load = new_safe_load
+ ruamel.yaml.safe_load_all = new_safe_load_all
+ ruamel.yaml.emit = new_emit
+ ruamel.yaml.serialize = new_serialize
+ ruamel.yaml.serialize_all = new_serialize_all
+ ruamel.yaml.dump = new_dump
+ ruamel.yaml.dump_all = new_dump_all
+ ruamel.yaml.safe_dump = new_safe_dump
+ ruamel.yaml.safe_dump_all = new_safe_dump_all
+
+
+def _tear_down():
+ ruamel.yaml.BaseLoader = ruamel.yaml.PyBaseLoader
+ ruamel.yaml.SafeLoader = ruamel.yaml.PySafeLoader
+ ruamel.yaml.Loader = ruamel.yaml.PyLoader
+ ruamel.yaml.BaseDumper = ruamel.yaml.PyBaseDumper
+ ruamel.yaml.SafeDumper = ruamel.yaml.PySafeDumper
+ ruamel.yaml.Dumper = ruamel.yaml.PyDumper
+ ruamel.yaml.scan = old_scan
+ ruamel.yaml.parse = old_parse
+ ruamel.yaml.compose = old_compose
+ ruamel.yaml.compose_all = old_compose_all
+ ruamel.yaml.load = old_load
+ ruamel.yaml.load_all = old_load_all
+ ruamel.yaml.safe_load = old_safe_load
+ ruamel.yaml.safe_load_all = old_safe_load_all
+ ruamel.yaml.emit = old_emit
+ ruamel.yaml.serialize = old_serialize
+ ruamel.yaml.serialize_all = old_serialize_all
+ ruamel.yaml.dump = old_dump
+ ruamel.yaml.dump_all = old_dump_all
+ ruamel.yaml.safe_dump = old_safe_dump
+ ruamel.yaml.safe_dump_all = old_safe_dump_all
+
+
+def test_c_version(verbose=False):
+ if verbose:
+ print(_ruamel_yaml.get_version())
+ print(_ruamel_yaml.get_version_string())
+ assert ('%s.%s.%s' % _ruamel_yaml.get_version()) == _ruamel_yaml.get_version_string(), (
+ _ruamel_yaml.get_version(),
+ _ruamel_yaml.get_version_string(),
+ )
+
+
+def _compare_scanners(py_data, c_data, verbose):
+ yaml = ruamel.yaml.YAML(typ='unsafe', pure=True)
+ py_tokens = list(yaml.scan(py_data, Loader=ruamel.yaml.PyLoader))
+ c_tokens = []
+ try:
+ yaml = ruamel.yaml.YAML(typ='unsafe', pure=False)
+ for token in yaml.scan(c_data, Loader=ruamel.yaml.CLoader):
+ c_tokens.append(token)
+ assert len(py_tokens) == len(c_tokens), (len(py_tokens), len(c_tokens))
+ for py_token, c_token in zip(py_tokens, c_tokens):
+ assert py_token.__class__ == c_token.__class__, (py_token, c_token)
+ if hasattr(py_token, 'value'):
+ assert py_token.value == c_token.value, (py_token, c_token)
+ if isinstance(py_token, ruamel.yaml.StreamEndToken):
+ continue
+ py_start = (
+ py_token.start_mark.index,
+ py_token.start_mark.line,
+ py_token.start_mark.column,
+ )
+ py_end = (
+ py_token.end_mark.index,
+ py_token.end_mark.line,
+ py_token.end_mark.column,
+ )
+ c_start = (
+ c_token.start_mark.index,
+ c_token.start_mark.line,
+ c_token.start_mark.column,
+ )
+ c_end = (c_token.end_mark.index, c_token.end_mark.line, c_token.end_mark.column)
+ assert py_start == c_start, (py_start, c_start)
+ assert py_end == c_end, (py_end, c_end)
+ finally:
+ if verbose:
+ print('PY_TOKENS:')
+ pprint.pprint(py_tokens)
+ print('C_TOKENS:')
+ pprint.pprint(c_tokens)
+
+
+def test_c_scanner(data_filename, canonical_filename, verbose=False):
+ with open(data_filename, 'rb') as fp0:
+ with open(data_filename, 'rb') as fp1:
+ _compare_scanners(fp0, fp1, verbose)
+ with open(data_filename, 'rb') as fp0:
+ with open(data_filename, 'rb') as fp1:
+ _compare_scanners(fp0.read(), fp1.read(), verbose)
+ with open(canonical_filename, 'rb') as fp0:
+ with open(canonical_filename, 'rb') as fp1:
+ _compare_scanners(fp0, fp1, verbose)
+ with open(canonical_filename, 'rb') as fp0:
+ with open(canonical_filename, 'rb') as fp1:
+ _compare_scanners(fp0.read(), fp1.read(), verbose)
+
+
+test_c_scanner.unittest = ['.data', '.canonical']
+test_c_scanner.skip = ['.skip-ext']
+
+
+def _compare_parsers(py_data, c_data, verbose):
+ yaml = ruamel.yaml.YAML(typ='unsafe', pure=True)
+ py_events = list(yaml.parse(py_data, Loader=ruamel.yaml.PyLoader))
+ c_events = []
+ try:
+ yaml = ruamel.yaml.YAML(typ='unsafe', pure=False)
+ for event in yaml.parse(c_data, Loader=ruamel.yaml.CLoader):
+ c_events.append(event)
+ assert len(py_events) == len(c_events), (len(py_events), len(c_events))
+ for py_event, c_event in zip(py_events, c_events):
+ for attribute in [
+ '__class__',
+ 'anchor',
+ 'tag',
+ 'implicit',
+ 'value',
+ 'explicit',
+ 'version',
+ 'tags',
+ ]:
+ py_value = getattr(py_event, attribute, None)
+ c_value = getattr(c_event, attribute, None)
+ assert py_value == c_value, (py_event, c_event, attribute)
+ finally:
+ if verbose:
+ print('PY_EVENTS:')
+ pprint.pprint(py_events)
+ print('C_EVENTS:')
+ pprint.pprint(c_events)
+
+
+def test_c_parser(data_filename, canonical_filename, verbose=False):
+ with open(data_filename, 'rb') as fp0:
+ with open(data_filename, 'rb') as fp1:
+ _compare_parsers(fp0, fp1, verbose)
+ with open(data_filename, 'rb') as fp0:
+ with open(data_filename, 'rb') as fp1:
+ _compare_parsers(fp0.read(), fp1.read(), verbose)
+ with open(canonical_filename, 'rb') as fp0:
+ with open(canonical_filename, 'rb') as fp1:
+ _compare_parsers(fp0, fp1, verbose)
+ with open(canonical_filename, 'rb') as fp0:
+ with open(canonical_filename, 'rb') as fp1:
+ _compare_parsers(fp0.read(), fp1.read(), verbose)
+
+
+test_c_parser.unittest = ['.data', '.canonical']
+test_c_parser.skip = ['.skip-ext']
+
+
+def _compare_emitters(data, verbose):
+ yaml = ruamel.yaml.YAML(typ='unsafe', pure=True)
+ events = list(yaml.parse(py_data, Loader=ruamel.yaml.PyLoader))
+ c_data = yaml.emit(events, Dumper=ruamel.yaml.CDumper)
+ if verbose:
+ print(c_data)
+ py_events = list(yaml.parse(c_data, Loader=ruamel.yaml.PyLoader))
+ c_events = list(yaml.parse(c_data, Loader=ruamel.yaml.CLoader))
+ try:
+ assert len(events) == len(py_events), (len(events), len(py_events))
+ assert len(events) == len(c_events), (len(events), len(c_events))
+ for event, py_event, c_event in zip(events, py_events, c_events):
+ for attribute in [
+ '__class__',
+ 'anchor',
+ 'tag',
+ 'implicit',
+ 'value',
+ 'explicit',
+ 'version',
+ 'tags',
+ ]:
+ value = getattr(event, attribute, None)
+ py_value = getattr(py_event, attribute, None)
+ c_value = getattr(c_event, attribute, None)
+ if (
+ attribute == 'tag'
+ and value in [None, '!']
+ and py_value in [None, '!']
+ and c_value in [None, '!']
+ ):
+ continue
+ if attribute == 'explicit' and (py_value or c_value):
+ continue
+ assert value == py_value, (event, py_event, attribute)
+ assert value == c_value, (event, c_event, attribute)
+ finally:
+ if verbose:
+ print('EVENTS:')
+ pprint.pprint(events)
+ print('PY_EVENTS:')
+ pprint.pprint(py_events)
+ print('C_EVENTS:')
+ pprint.pprint(c_events)
+
+
+def test_c_emitter(data_filename, canonical_filename, verbose=False):
+ with open(data_filename, 'rb') as fp0:
+ _compare_emitters(fp0.read(), verbose)
+ with open(canonical_filename, 'rb') as fp0:
+ _compare_emitters(fp0.read(), verbose)
+
+
+test_c_emitter.unittest = ['.data', '.canonical']
+test_c_emitter.skip = ['.skip-ext']
+
+
+def wrap_ext_function(function):
+ def wrapper(*args, **kwds):
+ _set_up()
+ try:
+ function(*args, **kwds)
+ finally:
+ _tear_down()
+
+ wrapper.__name__ = '%s_ext' % function.__name__
+ wrapper.unittest = function.unittest
+ wrapper.skip = getattr(function, 'skip', []) + ['.skip-ext']
+ return wrapper
+
+
+def wrap_ext(collections):
+ functions = []
+ if not isinstance(collections, list):
+ collections = [collections]
+ for collection in collections:
+ if not isinstance(collection, dict):
+ collection = vars(collection)
+ for key in sorted(collection):
+ value = collection[key]
+ if isinstance(value, types.FunctionType) and hasattr(value, 'unittest'):
+ functions.append(wrap_ext_function(value))
+ for function in functions:
+ assert function.__name__ not in globals()
+ globals()[function.__name__] = function
+
+
+import test_tokens # NOQA
+import test_structure # NOQA
+import test_errors # NOQA
+import test_resolver # NOQA
+import test_constructor # NOQA
+import test_emitter # NOQA
+import test_representer # NOQA
+import test_recursive # NOQA
+import test_input_output # NOQA
+
+wrap_ext(
+ [
+ test_tokens,
+ test_structure,
+ test_errors,
+ test_resolver,
+ test_constructor,
+ test_emitter,
+ test_representer,
+ test_recursive,
+ test_input_output,
+ ]
+)
+
+if __name__ == '__main__':
+ import sys
+ import test_appliance
+
+ sys.exit(test_appliance.run(globals()))
diff --git a/_test/roundtrip.py b/_test/roundtrip.py
new file mode 100644
index 0000000..d36d704
--- /dev/null
+++ b/_test/roundtrip.py
@@ -0,0 +1,362 @@
+# coding: utf-8
+
+"""
+helper routines for testing round trip of commented YAML data
+"""
+import sys
+import textwrap
+import io
+from pathlib import Path
+
+from typing import Any, Optional, Union
+
+unset = object()
+
+
+def dedent(data: str) -> str:
+ try:
+ position_of_first_newline = data.index('\n')
+ for idx in range(position_of_first_newline):
+ if not data[idx].isspace():
+ raise ValueError
+ except ValueError:
+ pass
+ else:
+ data = data[position_of_first_newline + 1 :]
+ return textwrap.dedent(data)
+
+
+def round_trip_load(
+ inp: Any, preserve_quotes: Optional[bool] = None, version: Optional[Any] = None,
+) -> Any:
+ import ruamel.yaml # NOQA
+
+ dinp = dedent(inp)
+ yaml = ruamel.yaml.YAML()
+ yaml.preserve_quotes = preserve_quotes
+ yaml.version = version
+ return yaml.load(dinp)
+
+
+def round_trip_load_all(
+ inp: Any, preserve_quotes: Optional[bool] = None, version: Optional[Any] = None,
+) -> Any:
+ import ruamel.yaml # NOQA
+
+ dinp = dedent(inp)
+ yaml = ruamel.yaml.YAML()
+ yaml.preserve_quotes = preserve_quotes
+ yaml.version = version
+ return yaml.load_all(dinp)
+
+
+def round_trip_dump(
+ data: Any,
+ stream: Any = None, # *,
+ indent: Optional[int] = None,
+ block_seq_indent: Optional[int] = None,
+ default_flow_style: Any = unset,
+ top_level_colon_align: Any = None,
+ prefix_colon: Any = None,
+ explicit_start: Optional[bool] = None,
+ explicit_end: Optional[bool] = None,
+ version: Optional[Any] = None,
+ allow_unicode: bool = True,
+) -> Union[str, None]:
+ import ruamel.yaml # NOQA
+
+ yaml = ruamel.yaml.YAML()
+ yaml.indent(mapping=indent, sequence=indent, offset=block_seq_indent)
+ if default_flow_style is not unset:
+ yaml.default_flow_style = default_flow_style
+ yaml.top_level_colon_align = top_level_colon_align
+ yaml.prefix_colon = prefix_colon
+ yaml.explicit_start = explicit_start
+ yaml.explicit_end = explicit_end
+ yaml.version = version
+ yaml.allow_unicode = allow_unicode
+ if stream is not None:
+ yaml.dump(data, stream=stream)
+ return None
+ buf = io.StringIO()
+ yaml.dump(data, stream=buf)
+ return buf.getvalue()
+
+
+def round_trip_dump_all(
+ data: Any,
+ stream: Any = None, # *,
+ indent: Optional[int] = None,
+ block_seq_indent: Optional[int] = None,
+ default_flow_style: Any = unset,
+ top_level_colon_align: Any = None,
+ prefix_colon: Any = None,
+ explicit_start: Optional[bool] = None,
+ explicit_end: Optional[bool] = None,
+ version: Optional[Any] = None,
+ allow_unicode: bool = True,
+) -> Union[str, None]:
+ import ruamel.yaml # NOQA
+
+ yaml = ruamel.yaml.YAML()
+ yaml.indent(mapping=indent, sequence=indent, offset=block_seq_indent)
+ if default_flow_style is not unset:
+ yaml.default_flow_style = default_flow_style
+ yaml.top_level_colon_align = top_level_colon_align
+ yaml.prefix_colon = prefix_colon
+ yaml.explicit_start = explicit_start
+ yaml.explicit_end = explicit_end
+ yaml.version = version
+ yaml.allow_unicode = allow_unicode
+ if stream is not None:
+ yaml.dump(data, stream=stream)
+ return None
+ buf = io.StringIO()
+ yaml.dump_all(data, stream=buf)
+ return buf.getvalue()
+
+
+def diff(inp: str, outp: str, file_name: str = 'stdin') -> None:
+ import difflib
+
+ inl = inp.splitlines(True) # True for keepends
+ outl = outp.splitlines(True)
+ diff = difflib.unified_diff(inl, outl, file_name, 'round trip YAML')
+ # 2.6 difflib has trailing space on filename lines %-)
+ strip_trailing_space = sys.version_info < (2, 7)
+ for line in diff:
+ if strip_trailing_space and line[:4] in ['--- ', '+++ ']:
+ line = line.rstrip() + '\n'
+ sys.stdout.write(line)
+
+
+def round_trip(
+ inp: str,
+ outp: Optional[str] = None,
+ extra: Optional[str] = None,
+ intermediate: Any = None,
+ indent: Optional[int] = None,
+ block_seq_indent: Optional[int] = None,
+ default_flow_style: Any = unset,
+ top_level_colon_align: Any = None,
+ prefix_colon: Any = None,
+ preserve_quotes: Any = None,
+ explicit_start: Optional[bool] = None,
+ explicit_end: Optional[bool] = None,
+ version: Optional[Any] = None,
+ dump_data: Any = None,
+) -> Any:
+ """
+ inp: input string to parse
+ outp: expected output (equals input if not specified)
+ """
+ if outp is None:
+ outp = inp
+ doutp = dedent(outp)
+ if extra is not None:
+ doutp += extra
+ data = round_trip_load(inp, preserve_quotes=preserve_quotes)
+ if dump_data:
+ print('data', data)
+ if intermediate is not None:
+ if isinstance(intermediate, dict):
+ for k, v in intermediate.items():
+ if data[k] != v:
+ print('{0!r} <> {1!r}'.format(data[k], v))
+ raise ValueError
+ res = round_trip_dump(
+ data,
+ indent=indent,
+ block_seq_indent=block_seq_indent,
+ top_level_colon_align=top_level_colon_align,
+ prefix_colon=prefix_colon,
+ explicit_start=explicit_start,
+ explicit_end=explicit_end,
+ version=version,
+ )
+ assert isinstance(res, str)
+ if res != doutp:
+ diff(doutp, res, 'input string')
+ print('\nroundtrip data:\n', res, sep="")
+ assert res == doutp
+ res = round_trip_dump(
+ data,
+ indent=indent,
+ block_seq_indent=block_seq_indent,
+ top_level_colon_align=top_level_colon_align,
+ prefix_colon=prefix_colon,
+ explicit_start=explicit_start,
+ explicit_end=explicit_end,
+ version=version,
+ )
+ print('roundtrip second round data:\n', res, sep="")
+ assert res == doutp
+ return data
+
+
+def na_round_trip(
+ inp: str,
+ outp: Optional[str] = None,
+ extra: Optional[str] = None,
+ intermediate: Any = None,
+ indent: Optional[int] = None,
+ top_level_colon_align: Any = None,
+ prefix_colon: Any = None,
+ preserve_quotes: Any = None,
+ explicit_start: Optional[bool] = None,
+ explicit_end: Optional[bool] = None,
+ version: Optional[Any] = None,
+ dump_data: Any = None,
+) -> Any:
+ """
+ inp: input string to parse
+ outp: expected output (equals input if not specified)
+ """
+ inp = dedent(inp)
+ if outp is None:
+ outp = inp
+ if version is not None:
+ version = version
+ doutp = dedent(outp)
+ if extra is not None:
+ doutp += extra
+ yaml = YAML()
+ yaml.preserve_quotes = preserve_quotes
+ yaml.scalar_after_indicator = False # newline after every directives end
+ data = yaml.load(inp)
+ if dump_data:
+ print('data', data)
+ if intermediate is not None:
+ if isinstance(intermediate, dict):
+ for k, v in intermediate.items():
+ if data[k] != v:
+ print('{0!r} <> {1!r}'.format(data[k], v))
+ raise ValueError
+ yaml.indent = indent
+ yaml.top_level_colon_align = top_level_colon_align
+ yaml.prefix_colon = prefix_colon
+ yaml.explicit_start = explicit_start
+ yaml.explicit_end = explicit_end
+ res = yaml.dump(data, compare=doutp)
+ return res
+
+
+def YAML(**kw: Any) -> Any:
+ import ruamel.yaml # NOQA
+
+ class MyYAML(ruamel.yaml.YAML):
+ """auto dedent string parameters on load"""
+
+ def load(self, stream: Any) -> Any:
+ if isinstance(stream, str):
+ if stream and stream[0] == '\n':
+ stream = stream[1:]
+ stream = textwrap.dedent(stream)
+ return ruamel.yaml.YAML.load(self, stream)
+
+ def load_all(self, stream: Any) -> Any:
+ if isinstance(stream, str):
+ if stream and stream[0] == '\n':
+ stream = stream[1:]
+ stream = textwrap.dedent(stream)
+ for d in ruamel.yaml.YAML.load_all(self, stream):
+ yield d
+
+ def dump(self, data: Any, **kw: Any) -> Any: # type: ignore
+ from ruamel.yaml.compat import StringIO, BytesIO # NOQA
+
+ assert ('stream' in kw) ^ ('compare' in kw)
+ if 'stream' in kw:
+ return ruamel.yaml.YAML.dump(data, **kw)
+ lkw = kw.copy()
+ expected = textwrap.dedent(lkw.pop('compare'))
+ unordered_lines = lkw.pop('unordered_lines', False)
+ if expected and expected[0] == '\n':
+ expected = expected[1:]
+ lkw['stream'] = st = StringIO()
+ ruamel.yaml.YAML.dump(self, data, **lkw)
+ res = st.getvalue()
+ print(res)
+ if unordered_lines:
+ res = sorted(res.splitlines()) # type: ignore
+ expected = sorted(expected.splitlines()) # type: ignore
+ assert res == expected
+
+ def round_trip(self, stream: Any, **kw: Any) -> None:
+ from ruamel.yaml.compat import StringIO, BytesIO # NOQA
+
+ assert isinstance(stream, str)
+ lkw = kw.copy()
+ if stream and stream[0] == '\n':
+ stream = stream[1:]
+ stream = textwrap.dedent(stream)
+ data = ruamel.yaml.YAML.load(self, stream)
+ outp = lkw.pop('outp', stream)
+ lkw['stream'] = st = StringIO()
+ ruamel.yaml.YAML.dump(self, data, **lkw)
+ res = st.getvalue()
+ if res != outp:
+ diff(outp, res, 'input string')
+ assert res == outp
+
+ def round_trip_all(self, stream: Any, **kw: Any) -> None:
+ from ruamel.yaml.compat import StringIO, BytesIO # NOQA
+
+ assert isinstance(stream, str)
+ lkw = kw.copy()
+ if stream and stream[0] == '\n':
+ stream = stream[1:]
+ stream = textwrap.dedent(stream)
+ data = list(ruamel.yaml.YAML.load_all(self, stream))
+ outp = lkw.pop('outp', stream)
+ lkw['stream'] = st = StringIO()
+ ruamel.yaml.YAML.dump_all(self, data, **lkw)
+ res = st.getvalue()
+ if res != outp:
+ diff(outp, res, 'input string')
+ assert res == outp
+
+ return MyYAML(**kw)
+
+
+def save_and_run(
+ program: str,
+ base_dir: Optional[Any] = None,
+ output: Optional[Any] = None,
+ file_name: Optional[Any] = None,
+ optimized: bool = False,
+) -> int:
+ """
+ safe and run a python program, thereby circumventing any restrictions on module level
+ imports
+ """
+ from subprocess import check_output, STDOUT, CalledProcessError
+
+ if not hasattr(base_dir, 'hash'):
+ base_dir = Path(str(base_dir))
+ if file_name is None:
+ file_name = 'safe_and_run_tmp.py'
+ file_name = base_dir / file_name # type: ignore
+ file_name.write_text(dedent(program))
+
+ try:
+ cmd = [sys.executable, '-Wd']
+ if optimized:
+ cmd.append('-O')
+ cmd.append(str(file_name))
+ print('running:', *cmd)
+ # 3.5 needs strings
+ res = check_output(cmd, stderr=STDOUT, universal_newlines=True, cwd=str(base_dir))
+ if output is not None:
+ if '__pypy__' in sys.builtin_module_names:
+ res1 = res.splitlines(True)
+ res2 = [line for line in res1 if 'no version info' not in line]
+ res = ''.join(res2)
+ print('result: ', res, end='')
+ print('expected:', output, end='')
+ assert res == output
+ except CalledProcessError as exception:
+ print("##### Running '{} {}' FAILED #####".format(sys.executable, file_name))
+ print(exception.output)
+ return exception.returncode
+ return 0
diff --git a/_test/test_a_dedent.py b/_test/test_a_dedent.py
new file mode 100644
index 0000000..a4adf33
--- /dev/null
+++ b/_test/test_a_dedent.py
@@ -0,0 +1,49 @@
+# coding: utf-8
+
+from roundtrip import dedent # type: ignore
+
+
+class TestDedent:
+ def test_start_newline(self) -> None:
+ # fmt: off
+ x = dedent("""
+ 123
+ 456
+ """)
+ # fmt: on
+ assert x == '123\n 456\n'
+
+ def test_start_space_newline(self) -> None:
+ # special construct to prevent stripping of following whitespace
+ # fmt: off
+ x = dedent(" " """
+ 123
+ """)
+ # fmt: on
+ assert x == '123\n'
+
+ def test_start_no_newline(self) -> None:
+ # special construct to prevent stripping of following whitespac
+ x = dedent("""\
+ 123
+ 456
+ """)
+ assert x == '123\n 456\n'
+
+ def test_preserve_no_newline_at_end(self) -> None:
+ x = dedent("""
+ 123""")
+ assert x == '123'
+
+ def test_preserve_no_newline_at_all(self) -> None:
+ x = dedent("""\
+ 123""")
+ assert x == '123'
+
+ def test_multiple_dedent(self) -> None:
+ x = dedent(
+ dedent("""
+ 123
+ """),
+ )
+ assert x == '123\n'
diff --git a/_test/test_add_xxx.py b/_test/test_add_xxx.py
new file mode 100644
index 0000000..22d652a
--- /dev/null
+++ b/_test/test_add_xxx.py
@@ -0,0 +1,188 @@
+# coding: utf-8
+
+import re
+import pytest # type: ignore # NOQA
+
+from roundtrip import dedent, round_trip_dump # type: ignore # NOQA
+from typing import Any
+
+
+# from PyYAML docs
+class Dice(tuple): # type: ignore
+ def __new__(cls, a: int, b: int) -> "Dice":
+ return tuple.__new__(cls, [a, b])
+
+ def __repr__(self) -> str:
+ return 'Dice(%s,%s)' % self
+
+
+def dice_constructor(loader: Any, node: Any) -> Dice:
+ value = loader.construct_scalar(node)
+ a, b = map(int, value.split('d'))
+ return Dice(a, b)
+
+
+def dice_representer(dumper: Any, data: Any) -> Any:
+ return dumper.represent_scalar('!dice', '{}d{}'.format(*data))
+
+
+def test_dice_constructor() -> None:
+ import ruamel.yaml # NOQA
+
+ with pytest.warns(PendingDeprecationWarning):
+ yaml = ruamel.yaml.YAML(typ='unsafe', pure=True)
+ ruamel.yaml.add_constructor('!dice', dice_constructor)
+ data = yaml.load('initial hit points: !dice 8d4')
+ assert str(data) == "{'initial hit points': Dice(8,4)}"
+
+
+def test_dice_constructor_with_loader() -> None:
+ import ruamel.yaml # NOQA
+
+ with pytest.warns(PendingDeprecationWarning):
+ yaml = ruamel.yaml.YAML(typ='unsafe', pure=True)
+ ruamel.yaml.add_constructor('!dice', dice_constructor, Loader=ruamel.yaml.Loader)
+ data = yaml.load('initial hit points: !dice 8d4')
+ assert str(data) == "{'initial hit points': Dice(8,4)}"
+
+
+def test_dice_representer() -> None:
+ import ruamel.yaml # NOQA
+
+ with pytest.warns(PendingDeprecationWarning):
+ yaml = ruamel.yaml.YAML(typ='unsafe', pure=True)
+ yaml.default_flow_style = False
+ ruamel.yaml.add_representer(Dice, dice_representer)
+ # ruamel.yaml 0.15.8+ no longer forces quotes tagged scalars
+ buf = ruamel.yaml.compat.StringIO()
+ yaml.dump(dict(gold=Dice(10, 6)), buf)
+ assert buf.getvalue() == 'gold: !dice 10d6\n'
+
+
+def test_dice_implicit_resolver() -> None:
+ import ruamel.yaml # NOQA
+
+ with pytest.warns(PendingDeprecationWarning):
+ yaml = ruamel.yaml.YAML(typ='unsafe', pure=True)
+ yaml.default_flow_style = False
+ pattern = re.compile(r'^\d+d\d+$')
+ ruamel.yaml.add_implicit_resolver('!dice', pattern)
+ buf = ruamel.yaml.compat.StringIO()
+ yaml.dump(dict(treasure=Dice(10, 20)), buf)
+ assert buf.getvalue() == 'treasure: 10d20\n'
+ assert yaml.load('damage: 5d10') == dict(damage=Dice(5, 10))
+
+
+class Obj1(dict): # type: ignore
+ def __init__(self, suffix: Any) -> None:
+ self._suffix = suffix
+ self._node = None
+
+ def add_node(self, n: Any) -> None:
+ self._node = n
+
+ def __repr__(self) -> str:
+ return 'Obj1(%s->%s)' % (self._suffix, self.items())
+
+ def dump(self) -> str:
+ return repr(self._node)
+
+
+class YAMLObj1:
+ yaml_tag = '!obj:'
+
+ @classmethod
+ def from_yaml(cls, loader: Any, suffix: Any, node: Any) -> Any:
+ import ruamel.yaml # NOQA
+
+ obj1 = Obj1(suffix)
+ if isinstance(node, ruamel.yaml.MappingNode):
+ obj1.add_node(loader.construct_mapping(node))
+ else:
+ raise NotImplementedError
+ return obj1
+
+ @classmethod
+ def to_yaml(cls, dumper: Any, data: Any) -> Any:
+ return dumper.represent_scalar(cls.yaml_tag + data._suffix, data.dump())
+
+
+def test_yaml_obj() -> None:
+ import ruamel.yaml # NOQA
+
+ with pytest.warns(PendingDeprecationWarning):
+ yaml = ruamel.yaml.YAML(typ='unsafe', pure=True)
+ ruamel.yaml.add_representer(Obj1, YAMLObj1.to_yaml)
+ ruamel.yaml.add_multi_constructor(YAMLObj1.yaml_tag, YAMLObj1.from_yaml)
+ x = yaml.load('!obj:x.2\na: 1')
+ print(x)
+ buf = ruamel.yaml.compat.StringIO()
+ yaml.dump(x, buf)
+ assert buf.getvalue() == """!obj:x.2 "{'a': 1}"\n"""
+
+
+def test_yaml_obj_with_loader_and_dumper() -> None:
+ import ruamel.yaml # NOQA
+
+ with pytest.warns(PendingDeprecationWarning):
+ yaml = ruamel.yaml.YAML(typ='unsafe', pure=True)
+ ruamel.yaml.add_representer(Obj1, YAMLObj1.to_yaml, Dumper=ruamel.yaml.Dumper)
+ ruamel.yaml.add_multi_constructor(
+ YAMLObj1.yaml_tag, YAMLObj1.from_yaml, Loader=ruamel.yaml.Loader,
+ )
+ x = yaml.load('!obj:x.2\na: 1')
+ # x = ruamel.yaml.load('!obj:x.2\na: 1')
+ print(x)
+ buf = ruamel.yaml.compat.StringIO()
+ yaml.dump(x, buf)
+ assert buf.getvalue() == """!obj:x.2 "{'a': 1}"\n"""
+
+
+# ToDo use nullege to search add_multi_representer and add_path_resolver
+# and add some test code
+
+# Issue 127 reported by Tommy Wang
+
+
+def test_issue_127() -> None:
+ import ruamel.yaml # NOQA
+
+ class Ref(ruamel.yaml.YAMLObject):
+ yaml_constructor = ruamel.yaml.RoundTripConstructor # type: ignore
+ yaml_representer = ruamel.yaml.RoundTripRepresenter # type: ignore
+ yaml_tag = '!Ref'
+
+ def __init__(self, logical_id: Any) -> None:
+ self.logical_id = logical_id
+
+ @classmethod
+ def from_yaml(cls, loader: Any, node: Any) -> Any:
+ return cls(loader.construct_scalar(node))
+
+ @classmethod
+ def to_yaml(cls, dumper: Any, data: Any) -> Any:
+ if isinstance(data.logical_id, ruamel.yaml.scalarstring.ScalarString):
+ style = data.logical_id.style # type: ignore # ruamel.yaml>0.15.8
+ else:
+ style = None
+ return dumper.represent_scalar(cls.yaml_tag, data.logical_id, style=style)
+
+ document = dedent("""\
+ AList:
+ - !Ref One
+ - !Ref 'Two'
+ - !Ref
+ Two and a half
+ BList: [!Ref Three, !Ref "Four"]
+ CList:
+ - Five Six
+ - 'Seven Eight'
+ """)
+ yaml = ruamel.yaml.YAML()
+ yaml.preserve_quotes = True
+ yaml.default_flow_style = None
+ yaml.indent(sequence=4, offset=2)
+ data = yaml.load(document)
+ buf = ruamel.yaml.compat.StringIO()
+ yaml.dump(data, buf)
+ assert buf.getvalue() == document.replace('\n Two and', ' Two and')
diff --git a/_test/test_anchor.py b/_test/test_anchor.py
new file mode 100644
index 0000000..d4da89b
--- /dev/null
+++ b/_test/test_anchor.py
@@ -0,0 +1,540 @@
+# coding: utf-8
+
+"""
+testing of anchors and the aliases referring to them
+"""
+
+import pytest # type: ignore # NOQA
+import platform
+
+from roundtrip import round_trip, dedent, round_trip_load, round_trip_dump, YAML # type: ignore # NOQA
+from typing import Any
+
+
+def load(s: str) -> Any:
+ return round_trip_load(dedent(s))
+
+
+def compare(d: Any, s: str) -> None:
+ assert round_trip_dump(d) == dedent(s)
+
+
+class TestAnchorsAliases:
+ def test_anchor_id_renumber(self) -> None:
+ from ruamel.yaml.serializer import Serializer
+
+ assert Serializer.ANCHOR_TEMPLATE == 'id{:03d}'
+ data = load("""
+ a: &id002
+ b: 1
+ c: 2
+ d: *id002
+ """)
+ compare(
+ data,
+ """
+ a: &id001
+ b: 1
+ c: 2
+ d: *id001
+ """,
+ )
+
+ def test_template_matcher(self) -> None:
+ """test if id matches the anchor template"""
+ from ruamel.yaml.serializer import templated_id
+
+ assert templated_id('id001')
+ assert templated_id('id999')
+ assert templated_id('id1000')
+ assert templated_id('id0001')
+ assert templated_id('id0000')
+ assert not templated_id('id02')
+ assert not templated_id('id000')
+ assert not templated_id('x000')
+
+ # def test_re_matcher(self) -> None:
+ # import re
+ # assert re.compile('id(?!000)\\d{3,}').match('id001')
+ # assert not re.compile('id(?!000\\d*)\\d{3,}').match('id000')
+ # assert re.compile('id(?!000$)\\d{3,}').match('id0001')
+
+ def test_anchor_assigned(self) -> None:
+ from ruamel.yaml.comments import CommentedMap
+
+ data = load("""
+ a: &id002
+ b: 1
+ c: 2
+ d: *id002
+ e: &etemplate
+ b: 1
+ c: 2
+ f: *etemplate
+ """)
+ d = data['d']
+ assert isinstance(d, CommentedMap)
+ assert d.yaml_anchor() is None # got dropped as it matches pattern
+ e = data['e']
+ assert isinstance(e, CommentedMap)
+ assert e.yaml_anchor().value == 'etemplate'
+ assert e.yaml_anchor().always_dump is False
+
+ def test_anchor_id_retained(self) -> None:
+ data = load("""
+ a: &id002
+ b: 1
+ c: 2
+ d: *id002
+ e: &etemplate
+ b: 1
+ c: 2
+ f: *etemplate
+ """)
+ compare(
+ data,
+ """
+ a: &id001
+ b: 1
+ c: 2
+ d: *id001
+ e: &etemplate
+ b: 1
+ c: 2
+ f: *etemplate
+ """,
+ )
+
+ @pytest.mark.skipif( # type: ignore
+ platform.python_implementation() == 'Jython', reason='Jython throws RepresenterError',
+ )
+ def test_alias_before_anchor(self) -> None:
+ from ruamel.yaml.composer import ComposerError
+
+ with pytest.raises(ComposerError):
+ data = load("""
+ d: *id002
+ a: &id002
+ b: 1
+ c: 2
+ """)
+ data = data
+
+ def test_anchor_on_sequence(self) -> None:
+ # as reported by Bjorn Stabell
+ # https://bitbucket.org/ruamel/yaml/issue/7/anchor-names-not-preserved
+ from ruamel.yaml.comments import CommentedSeq
+
+ data = load("""
+ nut1: &alice
+ - 1
+ - 2
+ nut2: &blake
+ - some data
+ - *alice
+ nut3:
+ - *blake
+ - *alice
+ """)
+ r = data['nut1']
+ assert isinstance(r, CommentedSeq)
+ assert r.yaml_anchor() is not None
+ assert r.yaml_anchor().value == 'alice'
+
+ merge_yaml = dedent("""
+ - &CENTER {x: 1, y: 2}
+ - &LEFT {x: 0, y: 2}
+ - &BIG {r: 10}
+ - &SMALL {r: 1}
+ # All the following maps are equal:
+ # Explicit keys
+ - x: 1
+ y: 2
+ r: 10
+ label: center/small
+ # Merge one map
+ - <<: *CENTER
+ r: 10
+ label: center/medium
+ # Merge multiple maps
+ - <<: [*CENTER, *BIG]
+ label: center/big
+ # Override
+ - <<: [*BIG, *LEFT, *SMALL]
+ x: 1
+ label: center/huge
+ """)
+
+ def test_merge_00(self) -> None:
+ data = load(self.merge_yaml)
+ d = data[4]
+ ok = True
+ for k in d:
+ for o in [5, 6, 7]:
+ x = d.get(k)
+ y = data[o].get(k)
+ if not isinstance(x, int):
+ x = x.split('/')[0]
+ y = y.split('/')[0]
+ if x != y:
+ ok = False
+ print('key', k, d.get(k), data[o].get(k))
+ assert ok
+
+ def test_merge_accessible(self) -> None:
+ from ruamel.yaml.comments import CommentedMap, merge_attrib
+
+ data = load("""
+ k: &level_2 { a: 1, b2 }
+ l: &level_1 { a: 10, c: 3 }
+ m:
+ <<: *level_1
+ c: 30
+ d: 40
+ """)
+ d = data['m']
+ assert isinstance(d, CommentedMap)
+ assert hasattr(d, merge_attrib)
+
+ def test_merge_01(self) -> None:
+ data = load(self.merge_yaml)
+ compare(data, self.merge_yaml)
+
+ def test_merge_nested(self) -> None:
+ yaml = """
+ a:
+ <<: &content
+ 1: plugh
+ 2: plover
+ 0: xyzzy
+ b:
+ <<: *content
+ """
+ data = round_trip(yaml) # NOQA
+
+ def test_merge_nested_with_sequence(self) -> None:
+ yaml = """
+ a:
+ <<: &content
+ <<: &y2
+ 1: plugh
+ 2: plover
+ 0: xyzzy
+ b:
+ <<: [*content, *y2]
+ """
+ data = round_trip(yaml) # NOQA
+
+ def test_add_anchor(self) -> None:
+ from ruamel.yaml.comments import CommentedMap
+
+ data = CommentedMap()
+ data_a = CommentedMap()
+ data['a'] = data_a
+ data_a['c'] = 3
+ data['b'] = 2
+ data.yaml_set_anchor('klm', always_dump=True)
+ data['a'].yaml_set_anchor('xyz', always_dump=True)
+ compare(
+ data,
+ """
+ &klm
+ a: &xyz
+ c: 3
+ b: 2
+ """,
+ )
+
+ # this is an error in PyYAML
+ def test_reused_anchor(self) -> None:
+ from ruamel.yaml.error import ReusedAnchorWarning
+
+ yaml = """
+ - &a
+ x: 1
+ - <<: *a
+ - &a
+ x: 2
+ - <<: *a
+ """
+ with pytest.warns(ReusedAnchorWarning):
+ data = round_trip(yaml) # NOQA
+
+ def test_issue_130(self) -> None:
+ # issue 130 reported by Devid Fee
+ import ruamel.yaml
+
+ ys = dedent("""\
+ components:
+ server: &server_component
+ type: spark.server:ServerComponent
+ host: 0.0.0.0
+ port: 8000
+ shell: &shell_component
+ type: spark.shell:ShellComponent
+
+ services:
+ server: &server_service
+ <<: *server_component
+ shell: &shell_service
+ <<: *shell_component
+ components:
+ server: {<<: *server_service}
+ """)
+ yaml = ruamel.yaml.YAML(typ='safe', pure=True)
+ data = yaml.load(ys)
+ assert data['services']['shell']['components']['server']['port'] == 8000
+
+ def test_issue_130a(self) -> None:
+ # issue 130 reported by Devid Fee
+ import ruamel.yaml
+
+ ys = dedent("""\
+ components:
+ server: &server_component
+ type: spark.server:ServerComponent
+ host: 0.0.0.0
+ port: 8000
+ shell: &shell_component
+ type: spark.shell:ShellComponent
+
+ services:
+ server: &server_service
+ <<: *server_component
+ port: 4000
+ shell: &shell_service
+ <<: *shell_component
+ components:
+ server: {<<: *server_service}
+ """)
+ yaml = ruamel.yaml.YAML(typ='safe', pure=True)
+ data = yaml.load(ys)
+ assert data['services']['shell']['components']['server']['port'] == 4000
+
+
+class TestMergeKeysValues:
+
+ yaml_str = dedent("""\
+ - &mx
+ a: x1
+ b: x2
+ c: x3
+ - &my
+ a: y1
+ b: y2 # masked by the one in &mx
+ d: y4
+ -
+ a: 1
+ <<: [*mx, *my]
+ m: 6
+ """)
+
+ # in the following d always has "expanded" the merges
+
+ def test_merge_for(self) -> None:
+ from ruamel.yaml import YAML
+
+ d = YAML(typ='safe', pure=True).load(self.yaml_str)
+ data = round_trip_load(self.yaml_str)
+ count = 0
+ for x in data[2]:
+ count += 1
+ print(count, x)
+ assert count == len(d[2])
+
+ def test_merge_keys(self) -> None:
+ from ruamel.yaml import YAML
+
+ d = YAML(typ='safe', pure=True).load(self.yaml_str)
+ data = round_trip_load(self.yaml_str)
+ count = 0
+ for x in data[2].keys():
+ count += 1
+ print(count, x)
+ assert count == len(d[2])
+
+ def test_merge_values(self) -> None:
+ from ruamel.yaml import YAML
+
+ d = YAML(typ='safe', pure=True).load(self.yaml_str)
+ data = round_trip_load(self.yaml_str)
+ count = 0
+ for x in data[2].values():
+ count += 1
+ print(count, x)
+ assert count == len(d[2])
+
+ def test_merge_items(self) -> None:
+ from ruamel.yaml import YAML
+
+ d = YAML(typ='safe', pure=True).load(self.yaml_str)
+ data = round_trip_load(self.yaml_str)
+ count = 0
+ for x in data[2].items():
+ count += 1
+ print(count, x)
+ assert count == len(d[2])
+
+ def test_len_items_delete(self) -> None:
+ from ruamel.yaml import YAML
+
+ d = YAML(typ='safe', pure=True).load(self.yaml_str)
+ data = round_trip_load(self.yaml_str)
+ x = data[2].items()
+ print('d2 items', d[2].items(), len(d[2].items()), x, len(x))
+ ref = len(d[2].items())
+ print('ref', ref)
+ assert len(x) == ref
+ del data[2]['m']
+ ref -= 1
+ assert len(x) == ref
+ del data[2]['d']
+ ref -= 1
+ assert len(x) == ref
+ del data[2]['a']
+ ref -= 1
+ assert len(x) == ref
+
+ def test_issue_196_cast_of_dict(self, capsys: Any) -> None:
+ from ruamel.yaml import YAML
+
+ yaml = YAML()
+ mapping = yaml.load("""\
+ anchored: &anchor
+ a : 1
+
+ mapping:
+ <<: *anchor
+ b: 2
+ """)['mapping']
+
+ for k in mapping:
+ print('k', k)
+ for k in mapping.copy():
+ print('kc', k)
+
+ print('v', list(mapping.keys()))
+ print('v', list(mapping.values()))
+ print('v', list(mapping.items()))
+ print(len(mapping))
+ print('-----')
+
+ # print({**mapping})
+ # print(type({**mapping}))
+ # assert 'a' in {**mapping}
+ assert 'a' in mapping
+ x = {}
+ for k in mapping:
+ x[k] = mapping[k]
+ assert 'a' in x
+ assert 'a' in mapping.keys()
+ assert mapping['a'] == 1
+ assert mapping.__getitem__('a') == 1
+ assert 'a' in dict(mapping)
+ assert 'a' in dict(mapping.items())
+
+ def test_values_of_merged(self) -> None:
+ from ruamel.yaml import YAML
+
+ yaml = YAML()
+ data = yaml.load(dedent(self.yaml_str))
+ assert list(data[2].values()) == [1, 6, 'x2', 'x3', 'y4']
+
+ def test_issue_213_copy_of_merge(self) -> None:
+ from ruamel.yaml import YAML
+
+ yaml = YAML()
+ d = yaml.load("""\
+ foo: &foo
+ a: a
+ foo2:
+ <<: *foo
+ b: b
+ """)['foo2']
+ assert d['a'] == 'a'
+ d2 = d.copy()
+ assert d2['a'] == 'a'
+ print('d', d)
+ del d['a']
+ assert 'a' not in d
+ assert 'a' in d2
+
+
+class TestDuplicateKeyThroughAnchor:
+ def test_duplicate_key_00(self) -> None:
+ from ruamel.yaml import version_info
+ from ruamel.yaml import YAML
+ from ruamel.yaml.constructor import DuplicateKeyFutureWarning, DuplicateKeyError
+
+ s = dedent("""\
+ &anchor foo:
+ foo: bar
+ *anchor : duplicate key
+ baz: bat
+ *anchor : duplicate key
+ """)
+ if version_info < (0, 15, 1):
+ pass
+ elif version_info < (0, 16, 0):
+ with pytest.warns(DuplicateKeyFutureWarning):
+ YAML(typ='safe', pure=True).load(s)
+ with pytest.warns(DuplicateKeyFutureWarning):
+ YAML(typ='rt').load(s)
+ else:
+ with pytest.raises(DuplicateKeyError):
+ YAML(typ='safe', pure=True).load(s)
+ with pytest.raises(DuplicateKeyError):
+ YAML(typ='rt').load(s)
+
+ def test_duplicate_key_01(self) -> None:
+ # so issue https://stackoverflow.com/a/52852106/1307905
+ from ruamel.yaml import version_info
+ from ruamel.yaml.constructor import DuplicateKeyError
+
+ s = dedent("""\
+ - &name-name
+ a: 1
+ - &help-name
+ b: 2
+ - <<: *name-name
+ <<: *help-name
+ """)
+ if version_info < (0, 15, 1):
+ pass
+ else:
+ with pytest.raises(DuplicateKeyError):
+ yaml = YAML(typ='safe')
+ yaml.load(s)
+ with pytest.raises(DuplicateKeyError):
+ yaml = YAML()
+ yaml.load(s)
+
+
+class TestFullCharSetAnchors:
+ def test_master_of_orion(self) -> None:
+ # https://bitbucket.org/ruamel/yaml/issues/72/not-allowed-in-anchor-names
+ # submitted by Shalon Wood
+ yaml_str = """
+ - collection: &Backend.Civilizations.RacialPerk
+ items:
+ - key: perk_population_growth_modifier
+ - *Backend.Civilizations.RacialPerk
+ """
+ data = load(yaml_str) # NOQA
+
+ def test_roundtrip_00(self) -> None:
+ yaml_str = """
+ - &dotted.words.here
+ a: 1
+ b: 2
+ - *dotted.words.here
+ """
+ data = round_trip(yaml_str) # NOQA
+
+ def test_roundtrip_01(self) -> None:
+ yaml_str = """
+ - &dotted.words.here[a, b]
+ - *dotted.words.here
+ """
+ data = load(yaml_str) # NOQA
+ compare(data, yaml_str.replace('[', ' [')) # an extra space is inserted
diff --git a/_test/test_api_change.py b/_test/test_api_change.py
new file mode 100644
index 0000000..85d3db8
--- /dev/null
+++ b/_test/test_api_change.py
@@ -0,0 +1,239 @@
+# coding: utf-8
+
+"""
+testing of anchors and the aliases referring to them
+"""
+
+import sys
+import textwrap
+import pytest # type: ignore
+from pathlib import Path
+
+from typing import Any
+
+
+class TestNewAPI:
+ def test_duplicate_keys_00(self) -> None:
+ from ruamel.yaml import YAML
+ from ruamel.yaml.constructor import DuplicateKeyError
+
+ yaml = YAML()
+ with pytest.raises(DuplicateKeyError):
+ yaml.load('{a: 1, a: 2}')
+
+ def test_duplicate_keys_01(self) -> None:
+ from ruamel.yaml import YAML
+ from ruamel.yaml.constructor import DuplicateKeyError
+
+ yaml = YAML(typ='safe', pure=True)
+ with pytest.raises(DuplicateKeyError):
+ yaml.load('{a: 1, a: 2}')
+
+ def test_duplicate_keys_02(self) -> None:
+ from ruamel.yaml import YAML
+ from ruamel.yaml.constructor import DuplicateKeyError
+
+ yaml = YAML(typ='safe')
+ with pytest.raises(DuplicateKeyError):
+ yaml.load('{a: 1, a: 2}')
+
+ def test_issue_135(self) -> None:
+ # reported by Andrzej Ostrowski
+ from ruamel.yaml import YAML
+
+ data = {'a': 1, 'b': 2}
+ yaml = YAML(typ='safe')
+ # originally on 2.7: with pytest.raises(TypeError):
+ yaml.dump(data, sys.stdout)
+
+ def test_issue_135_temporary_workaround(self) -> None:
+ # never raised error
+ from ruamel.yaml import YAML
+
+ data = {'a': 1, 'b': 2}
+ yaml = YAML(typ='safe', pure=True)
+ yaml.dump(data, sys.stdout)
+
+
+class TestWrite:
+ def test_dump_path(self, tmpdir: Any) -> None:
+ from ruamel.yaml import YAML
+
+ fn = Path(str(tmpdir)) / 'test.yaml'
+ yaml = YAML()
+ data = yaml.map()
+ data['a'] = 1
+ data['b'] = 2
+ yaml.dump(data, fn)
+ assert fn.read_text() == 'a: 1\nb: 2\n'
+
+ def test_dump_file(self, tmpdir: Any) -> None:
+ from ruamel.yaml import YAML
+
+ fn = Path(str(tmpdir)) / 'test.yaml'
+ yaml = YAML()
+ data = yaml.map()
+ data['a'] = 1
+ data['b'] = 2
+ with open(str(fn), 'w') as fp:
+ yaml.dump(data, fp)
+ assert fn.read_text() == 'a: 1\nb: 2\n'
+
+ def test_dump_missing_stream(self) -> None:
+ from ruamel.yaml import YAML
+
+ yaml = YAML()
+ data = yaml.map()
+ data['a'] = 1
+ data['b'] = 2
+ with pytest.raises(TypeError):
+ yaml.dump(data)
+
+ def test_dump_too_many_args(self, tmpdir: Any) -> None:
+ from ruamel.yaml import YAML
+
+ fn = Path(str(tmpdir)) / 'test.yaml'
+ yaml = YAML()
+ data = yaml.map()
+ data['a'] = 1
+ data['b'] = 2
+ with pytest.raises(TypeError):
+ yaml.dump(data, fn, True) # type: ignore
+
+ def test_transform(self, tmpdir: Any) -> None:
+ from ruamel.yaml import YAML
+
+ def tr(s: str) -> str:
+ return s.replace(' ', ' ')
+
+ fn = Path(str(tmpdir)) / 'test.yaml'
+ yaml = YAML()
+ data = yaml.map()
+ data['a'] = 1
+ data['b'] = 2
+ yaml.dump(data, fn, transform=tr)
+ assert fn.read_text() == 'a: 1\nb: 2\n'
+
+ def test_print(self, capsys: Any) -> None:
+ from ruamel.yaml import YAML
+
+ yaml = YAML()
+ data = yaml.map()
+ data['a'] = 1
+ data['b'] = 2
+ yaml.dump(data, sys.stdout)
+ out, err = capsys.readouterr()
+ assert out == 'a: 1\nb: 2\n'
+
+
+class TestRead:
+ def test_multi_load(self) -> None:
+ # make sure reader, scanner, parser get reset
+ from ruamel.yaml import YAML
+
+ yaml = YAML()
+ yaml.load('a: 1')
+ yaml.load('a: 1') # did not work in 0.15.4
+
+ def test_parse(self) -> None:
+ # ensure `parse` method is functional and can parse "unsafe" yaml
+ from ruamel.yaml import YAML
+ from ruamel.yaml.constructor import ConstructorError
+
+ yaml = YAML(typ='safe')
+ s = '- !User0 {age: 18, name: Anthon}'
+ # should fail to load
+ with pytest.raises(ConstructorError):
+ yaml.load(s)
+ # should parse fine
+ yaml = YAML(typ='safe')
+ for _ in yaml.parse(s):
+ pass
+
+
+class TestLoadAll:
+ def test_multi_document_load(self, tmpdir: Any) -> None:
+ """this went wrong on 3.7 because of StopIteration, PR 37 and Issue 211"""
+ from ruamel.yaml import YAML
+
+ fn = Path(str(tmpdir)) / 'test.yaml'
+ fn.write_text(
+ textwrap.dedent("""\
+ ---
+ - a
+ ---
+ - b
+ ...
+ """),
+ )
+ yaml = YAML()
+ assert list(yaml.load_all(fn)) == [['a'], ['b']]
+
+
+class TestDuplSet:
+ def test_dupl_set_00(self) -> None:
+ # round-trip-loader should except
+ from ruamel.yaml import YAML
+ from ruamel.yaml.constructor import DuplicateKeyError
+
+ yaml = YAML()
+ with pytest.raises(DuplicateKeyError):
+ yaml.load(
+ textwrap.dedent("""\
+ !!set
+ ? a
+ ? b
+ ? c
+ ? a
+ """),
+ )
+
+
+class TestDumpLoadUnicode:
+ # test triggered by SamH on stackoverflow (https://stackoverflow.com/q/45281596/1307905)
+ # and answer by randomir (https://stackoverflow.com/a/45281922/1307905)
+ def test_write_unicode(self, tmpdir: Any) -> None:
+ from ruamel.yaml import YAML
+
+ yaml = YAML()
+ text_dict = {'text': 'HELLO_WORLD©'}
+ file_name = str(tmpdir) + '/tstFile.yaml'
+ yaml.dump(text_dict, open(file_name, 'w'))
+ assert open(file_name, 'rb').read().decode('utf-8') == 'text: HELLO_WORLD©\n'
+
+ def test_read_unicode(self, tmpdir: Any) -> None:
+ from ruamel.yaml import YAML
+
+ yaml = YAML()
+ file_name = str(tmpdir) + '/tstFile.yaml'
+ with open(file_name, 'wb') as fp:
+ fp.write('text: HELLO_WORLD©\n'.encode('utf-8'))
+ text_dict = yaml.load(open(file_name, 'r'))
+ print(text_dict)
+ assert text_dict['text'] == 'HELLO_WORLD©'
+
+
+class TestFlowStyle:
+ def test_flow_style(self, capsys: Any) -> None:
+ # https://stackoverflow.com/questions/45791712/
+ from ruamel.yaml import YAML
+
+ yaml = YAML()
+ yaml.default_flow_style = None
+ data = yaml.map()
+ data['b'] = 1
+ data['a'] = [[1, 2], [3, 4]]
+ yaml.dump(data, sys.stdout)
+ out, err = capsys.readouterr()
+ assert out == 'b: 1\na:\n- [1, 2]\n- [3, 4]\n'
+
+
+class TestOldAPI:
+ @pytest.mark.skipif(sys.version_info >= (3, 0), reason='ok on Py3') # type: ignore
+ def test_duplicate_keys_02(self) -> None:
+ # Issue 165 unicode keys in error/warning
+ from ruamel.yaml import safe_load
+ from ruamel.yaml.constructor import DuplicateKeyError
+
+ with pytest.raises(DuplicateKeyError):
+ safe_load('type: Doméstica\ntype: International')
diff --git a/_test/test_class_register.py b/_test/test_class_register.py
new file mode 100644
index 0000000..b15ef77
--- /dev/null
+++ b/_test/test_class_register.py
@@ -0,0 +1,145 @@
+# coding: utf-8
+
+"""
+testing of YAML.register_class and @yaml_object
+"""
+
+import pytest # type: ignore # NOQA
+from typing import Any
+from ruamel.yaml.comments import TaggedScalar, CommentedMap # NOQA
+
+from roundtrip import YAML # type: ignore
+
+
+class User0:
+ def __init__(self, name: str, age: int) -> None:
+ self.name = name
+ self.age = age
+
+
+class User1:
+ yaml_tag = '!user'
+
+ def __init__(self, name: str, age: int) -> None:
+ self.name = name
+ self.age = age
+
+ @classmethod
+ def to_yaml(cls, representer: Any, node: Any) -> Any:
+ return representer.represent_scalar(cls.yaml_tag, '{.name}-{.age}'.format(node, node))
+
+ @classmethod
+ def from_yaml(cls, constructor: Any, node: Any) -> Any:
+ return cls(*node.value.split('-'))
+
+
+class TestRegisterClass:
+ def test_register_0_rt(self) -> None:
+ yaml = YAML()
+ yaml.register_class(User0)
+ ys = """
+ - !User0
+ name: Anthon
+ age: 18
+ """
+ d = yaml.load(ys)
+ yaml.dump(d, compare=ys, unordered_lines=True)
+
+ def test_register_0_safe(self) -> None:
+ # default_flow_style = None
+ yaml = YAML(typ='safe')
+ yaml.register_class(User0)
+ ys = """
+ - !User0 {age: 18, name: Anthon}
+ """
+ d = yaml.load(ys)
+ yaml.dump(d, compare=ys)
+
+ def test_register_0_unsafe(self) -> None:
+ # default_flow_style = None
+ with pytest.warns(PendingDeprecationWarning):
+ yaml = YAML(typ='unsafe')
+ yaml.register_class(User0)
+ ys = """
+ - !User0 {age: 18, name: Anthon}
+ """
+ d = yaml.load(ys)
+ yaml.dump(d, compare=ys)
+
+ def test_register_1_rt(self) -> None:
+ yaml = YAML()
+ yaml.register_class(User1)
+ ys = """
+ - !user Anthon-18
+ """
+ d = yaml.load(ys)
+ yaml.dump(d, compare=ys)
+
+ def test_register_1_safe(self) -> None:
+ yaml = YAML(typ='safe')
+ yaml.register_class(User1)
+ ys = """
+ [!user Anthon-18]
+ """
+ d = yaml.load(ys)
+ yaml.dump(d, compare=ys)
+
+ def test_register_1_unsafe(self) -> None:
+ with pytest.warns(PendingDeprecationWarning):
+ yaml = YAML(typ='unsafe')
+ yaml.register_class(User1)
+ ys = """
+ [!user Anthon-18]
+ """
+ d = yaml.load(ys)
+ yaml.dump(d, compare=ys)
+
+
+class TestDecorator:
+ def test_decorator_implicit(self) -> None:
+ from ruamel.yaml import yaml_object
+
+ yml = YAML()
+
+ @yaml_object(yml)
+ class User2:
+ def __init__(self, name: str, age: int) -> None:
+ self.name = name
+ self.age = age
+
+ ys = """
+ - !User2
+ name: Anthon
+ age: 18
+ """
+ d = yml.load(ys)
+ yml.dump(d, compare=ys, unordered_lines=True)
+
+ def test_decorator_explicit(self) -> None:
+ from ruamel.yaml import yaml_object
+
+ yml = YAML()
+
+ @yaml_object(yml)
+ class User3:
+ yaml_tag = '!USER'
+
+ def __init__(self, name: str, age: int) -> None:
+ self.name = name
+ self.age = age
+
+ @classmethod
+ def to_yaml(cls, representer: Any, node: Any) -> Any:
+ return representer.represent_scalar(
+ cls.yaml_tag, '{.name}-{.age}'.format(node, node),
+ )
+
+ @classmethod
+ def from_yaml(cls, constructor: Any, node: Any) -> Any:
+ return cls(*node.value.split('-'))
+
+ ys = """
+ - !USER Anthon-18
+ """
+ d = yml.load(ys)
+ yml.dump(d, compare=ys)
diff --git a/_test/test_collections.py b/_test/test_collections.py
new file mode 100644
index 0000000..cee1ab3
--- /dev/null
+++ b/_test/test_collections.py
@@ -0,0 +1,20 @@
+# coding: utf-8
+
+"""
+collections.OrderedDict is a new class not supported by PyYAML (issue 83 by Frazer McLean)
+
+This is now so integrated in Python that it can be mapped to !!omap
+
+"""
+
+import pytest # type: ignore # NOQA
+
+
+from roundtrip import round_trip, dedent, round_trip_load, round_trip_dump # type: ignore # NOQA
+
+
+class TestOrderedDict:
+ def test_ordereddict(self) -> None:
+ from collections import OrderedDict
+
+ assert round_trip_dump(OrderedDict()) == '!!omap []\n'
diff --git a/_test/test_comment_manipulation.py b/_test/test_comment_manipulation.py
new file mode 100644
index 0000000..7ff26f7
--- /dev/null
+++ b/_test/test_comment_manipulation.py
@@ -0,0 +1,588 @@
+# coding: utf-8
+
+import pytest # type: ignore # NOQA
+
+from roundtrip import round_trip, dedent, round_trip_load, round_trip_dump # type: ignore # NOQA
+from typing import Any
+
+
+def load(s: str) -> Any:
+ return round_trip_load(dedent(s))
+
+
+def compare(data: Any, s: str, **kw: Any) -> None:
+ assert round_trip_dump(data, **kw) == dedent(s)
+
+
+def compare_eol(data: Any, s: str) -> None:
+ assert 'EOL' in s
+ ds = dedent(s).replace('EOL', '').replace('\n', '|\n')
+ assert round_trip_dump(data).replace('\n', '|\n') == ds
+
+
+class TestCommentsManipulation:
+
+ # list
+ def test_seq_set_comment_on_existing_explicit_column(self) -> None:
+ data = load("""
+ - a # comment 1
+ - b
+ - c
+ """)
+ data.yaml_add_eol_comment('comment 2', key=1, column=6)
+ exp = """
+ - a # comment 1
+ - b # comment 2
+ - c
+ """
+ compare(data, exp)
+
+ def test_seq_overwrite_comment_on_existing_explicit_column(self) -> None:
+ data = load("""
+ - a # comment 1
+ - b
+ - c
+ """)
+ data.yaml_add_eol_comment('comment 2', key=0, column=6)
+ exp = """
+ - a # comment 2
+ - b
+ - c
+ """
+ compare(data, exp)
+
+ def test_seq_first_comment_explicit_column(self) -> None:
+ data = load("""
+ - a
+ - b
+ - c
+ """)
+ data.yaml_add_eol_comment('comment 1', key=1, column=6)
+ exp = """
+ - a
+ - b # comment 1
+ - c
+ """
+ compare(data, exp)
+
+ def test_seq_set_comment_on_existing_column_prev(self) -> None:
+ data = load("""
+ - a # comment 1
+ - b
+ - c
+ - d # comment 3
+ """)
+ data.yaml_add_eol_comment('comment 2', key=1)
+ exp = """
+ - a # comment 1
+ - b # comment 2
+ - c
+ - d # comment 3
+ """
+ compare(data, exp)
+
+ def test_seq_set_comment_on_existing_column_next(self) -> None:
+ data = load("""
+ - a # comment 1
+ - b
+ - c
+ - d # comment 3
+ """)
+ print(data.ca)
+ # print(type(data._yaml_comment._items[0][0].start_mark))
+ # ruamel.yaml.error.Mark
+ # print(type(data._yaml_comment._items[0][0].start_mark))
+ data.yaml_add_eol_comment('comment 2', key=2)
+ exp = """
+ - a # comment 1
+ - b
+ - c # comment 2
+ - d # comment 3
+ """
+ compare(data, exp)
+
+ def test_seq_set_comment_on_existing_column_further_away(self) -> None:
+ """
+ no comment line before or after, take the latest before
+ the new position
+ """
+ data = load("""
+ - a # comment 1
+ - b
+ - c
+ - d
+ - e
+ - f # comment 3
+ """)
+ print(data.ca)
+ # print(type(data._yaml_comment._items[0][0].start_mark))
+ # ruamel.yaml.error.Mark
+ # print(type(data._yaml_comment._items[0][0].start_mark))
+ data.yaml_add_eol_comment('comment 2', key=3)
+ exp = """
+ - a # comment 1
+ - b
+ - c
+ - d # comment 2
+ - e
+ - f # comment 3
+ """
+ compare(data, exp)
+
+ def test_seq_set_comment_on_existing_explicit_column_with_hash(self) -> None:
+ data = load("""
+ - a # comment 1
+ - b
+ - c
+ """)
+ data.yaml_add_eol_comment('# comment 2', key=1, column=6)
+ exp = """
+ - a # comment 1
+ - b # comment 2
+ - c
+ """
+ compare(data, exp)
+
+ # dict
+
+ def test_dict_set_comment_on_existing_explicit_column(self) -> None:
+ data = load("""
+ a: 1 # comment 1
+ b: 2
+ c: 3
+ d: 4
+ e: 5
+ """)
+ data.yaml_add_eol_comment('comment 2', key='c', column=7)
+ exp = """
+ a: 1 # comment 1
+ b: 2
+ c: 3 # comment 2
+ d: 4
+ e: 5
+ """
+ compare(data, exp)
+
+ def test_dict_overwrite_comment_on_existing_explicit_column(self) -> None:
+ data = load("""
+ a: 1 # comment 1
+ b: 2
+ c: 3
+ d: 4
+ e: 5
+ """)
+ data.yaml_add_eol_comment('comment 2', key='a', column=7)
+ exp = """
+ a: 1 # comment 2
+ b: 2
+ c: 3
+ d: 4
+ e: 5
+ """
+ compare(data, exp)
+
+ def test_map_set_comment_on_existing_column_prev(self) -> None:
+ data = load("""
+ a: 1 # comment 1
+ b: 2
+ c: 3
+ d: 4
+ e: 5 # comment 3
+ """)
+ data.yaml_add_eol_comment('comment 2', key='b')
+ exp = """
+ a: 1 # comment 1
+ b: 2 # comment 2
+ c: 3
+ d: 4
+ e: 5 # comment 3
+ """
+ compare(data, exp)
+
+ def test_map_set_comment_on_existing_column_next(self) -> None:
+ data = load("""
+ a: 1 # comment 1
+ b: 2
+ c: 3
+ d: 4
+ e: 5 # comment 3
+ """)
+ data.yaml_add_eol_comment('comment 2', key='d')
+ exp = """
+ a: 1 # comment 1
+ b: 2
+ c: 3
+ d: 4 # comment 2
+ e: 5 # comment 3
+ """
+ compare(data, exp)
+
+ def test_map_set_comment_on_existing_column_further_away(self) -> None:
+ """
+ no comment line before or after, take the latest before
+ the new position
+ """
+ data = load("""
+ a: 1 # comment 1
+ b: 2
+ c: 3
+ d: 4
+ e: 5 # comment 3
+ """)
+ data.yaml_add_eol_comment('comment 2', key='c')
+ print(round_trip_dump(data))
+ exp = """
+ a: 1 # comment 1
+ b: 2
+ c: 3 # comment 2
+ d: 4
+ e: 5 # comment 3
+ """
+ compare(data, exp)
+
+ def test_before_top_map_rt(self) -> None:
+ data = load("""
+ a: 1
+ b: 2
+ """)
+ data.yaml_set_start_comment('Hello\nWorld\n')
+ exp = """
+ # Hello
+ # World
+ a: 1
+ b: 2
+ """
+ compare(data, exp.format(comment='#'))
+
+ def test_before_top_map_replace(self) -> None:
+ data = load("""
+ # abc
+ # def
+ a: 1 # 1
+ b: 2
+ """)
+ data.yaml_set_start_comment('Hello\nWorld\n')
+ exp = """
+ # Hello
+ # World
+ a: 1 # 1
+ b: 2
+ """
+ compare(data, exp.format(comment='#'))
+
+ def test_before_top_map_from_scratch(self) -> None:
+ from ruamel.yaml.comments import CommentedMap
+
+ data = CommentedMap()
+ data['a'] = 1
+ data['b'] = 2
+ data.yaml_set_start_comment('Hello\nWorld\n')
+ # print(data.ca)
+ # print(data.ca._items)
+ exp = """
+ # Hello
+ # World
+ a: 1
+ b: 2
+ """
+ compare(data, exp.format(comment='#'))
+
+ def test_before_top_seq_rt(self) -> None:
+ data = load("""
+ - a
+ - b
+ """)
+ data.yaml_set_start_comment('Hello\nWorld\n')
+ print(round_trip_dump(data))
+ exp = """
+ # Hello
+ # World
+ - a
+ - b
+ """
+ compare(data, exp)
+
+ def test_before_top_seq_rt_replace(self) -> None:
+ s = """
+ # this
+ # that
+ - a
+ - b
+ """
+ data = load(s.format(comment='#'))
+ data.yaml_set_start_comment('Hello\nWorld\n')
+ print(round_trip_dump(data))
+ exp = """
+ # Hello
+ # World
+ - a
+ - b
+ """
+ compare(data, exp.format(comment='#'))
+
+ def test_before_top_seq_from_scratch(self) -> None:
+ from ruamel.yaml.comments import CommentedSeq
+
+ data = CommentedSeq()
+ data.append('a')
+ data.append('b')
+ data.yaml_set_start_comment('Hello\nWorld\n')
+ print(round_trip_dump(data))
+ exp = """
+ # Hello
+ # World
+ - a
+ - b
+ """
+ compare(data, exp.format(comment='#'))
+
+ # nested variants
+ def test_before_nested_map_rt(self) -> None:
+ data = load("""
+ a: 1
+ b:
+ c: 2
+ d: 3
+ """)
+ data['b'].yaml_set_start_comment('Hello\nWorld\n')
+ exp = """
+ a: 1
+ b:
+ # Hello
+ # World
+ c: 2
+ d: 3
+ """
+ compare(data, exp.format(comment='#'))
+
+ def test_before_nested_map_rt_indent(self) -> None:
+ data = load("""
+ a: 1
+ b:
+ c: 2
+ d: 3
+ """)
+ data['b'].yaml_set_start_comment('Hello\nWorld\n', indent=2)
+ exp = """
+ a: 1
+ b:
+ # Hello
+ # World
+ c: 2
+ d: 3
+ """
+ compare(data, exp.format(comment='#'))
+ print(data['b'].ca)
+
+ def test_before_nested_map_from_scratch(self) -> None:
+ from ruamel.yaml.comments import CommentedMap
+
+ data = CommentedMap()
+ datab = CommentedMap()
+ data['a'] = 1
+ data['b'] = datab
+ datab['c'] = 2
+ datab['d'] = 3
+ data['b'].yaml_set_start_comment('Hello\nWorld\n')
+ exp = """
+ a: 1
+ b:
+ # Hello
+ # World
+ c: 2
+ d: 3
+ """
+ compare(data, exp.format(comment='#'))
+
+ def test_before_nested_seq_from_scratch(self) -> None:
+ from ruamel.yaml.comments import CommentedMap, CommentedSeq
+
+ data = CommentedMap()
+ datab = CommentedSeq()
+ data['a'] = 1
+ data['b'] = datab
+ datab.append('c')
+ datab.append('d')
+ data['b'].yaml_set_start_comment('Hello\nWorld\n', indent=2)
+ exp = """
+ a: 1
+ b:
+ # Hello
+ # World
+ - c
+ - d
+ """
+ compare(data, exp.format(comment='#'))
+
+ def test_before_nested_seq_from_scratch_block_seq_indent(self) -> None:
+ from ruamel.yaml.comments import CommentedMap, CommentedSeq
+
+ data = CommentedMap()
+ datab = CommentedSeq()
+ data['a'] = 1
+ data['b'] = datab
+ datab.append('c')
+ datab.append('d')
+ data['b'].yaml_set_start_comment('Hello\nWorld\n', indent=2)
+ exp = """
+ a: 1
+ b:
+ # Hello
+ # World
+ - c
+ - d
+ """
+ compare(data, exp.format(comment='#'), indent=4, block_seq_indent=2)
+
+ def test_map_set_comment_before_and_after_non_first_key_00(self) -> None:
+ # http://stackoverflow.com/a/40705671/1307905
+ data = load("""
+ xyz:
+ a: 1 # comment 1
+ b: 2
+
+ test1:
+ test2:
+ test3: 3
+ """)
+ data.yaml_set_comment_before_after_key(
+ 'test1', 'before test1 (top level)', after='before test2',
+ )
+ data['test1']['test2'].yaml_set_start_comment('after test2', indent=4)
+ exp = """
+ xyz:
+ a: 1 # comment 1
+ b: 2
+
+ # before test1 (top level)
+ test1:
+ # before test2
+ test2:
+ # after test2
+ test3: 3
+ """
+ compare(data, exp)
+
+ def Xtest_map_set_comment_before_and_after_non_first_key_01(self) -> None:
+ data = load("""
+ xyz:
+ a: 1 # comment 1
+ b: 2
+
+ test1:
+ test2:
+ test3: 3
+ """)
+ data.yaml_set_comment_before_after_key(
+ 'test1', 'before test1 (top level)', after='before test2\n\n',
+ )
+ data['test1']['test2'].yaml_set_start_comment('after test2', indent=4)
+ # EOL is needed here as dedenting gets rid of spaces (as well as does Emacs
+ exp = """
+ xyz:
+ a: 1 # comment 1
+ b: 2
+
+ # before test1 (top level)
+ test1:
+ # before test2
+ EOL
+ test2:
+ # after test2
+ test3: 3
+ """
+ compare_eol(data, exp)
+
+ # EOL is no longer necessary
+ # fixed together with issue # 216
+ def test_map_set_comment_before_and_after_non_first_key_01(self) -> None:
+ data = load("""
+ xyz:
+ a: 1 # comment 1
+ b: 2
+
+ test1:
+ test2:
+ test3: 3
+ """)
+ data.yaml_set_comment_before_after_key(
+ 'test1', 'before test1 (top level)', after='before test2\n\n',
+ )
+ data['test1']['test2'].yaml_set_start_comment('after test2', indent=4)
+ exp = """
+ xyz:
+ a: 1 # comment 1
+ b: 2
+
+ # before test1 (top level)
+ test1:
+ # before test2
+
+ test2:
+ # after test2
+ test3: 3
+ """
+ compare(data, exp)
+
+ def Xtest_map_set_comment_before_and_after_non_first_key_02(self) -> None:
+ data = load("""
+ xyz:
+ a: 1 # comment 1
+ b: 2
+
+ test1:
+ test2:
+ test3: 3
+ """)
+ data.yaml_set_comment_before_after_key(
+ 'test1', 'xyz\n\nbefore test1 (top level)', after='\nbefore test2', after_indent=4,
+ )
+ data['test1']['test2'].yaml_set_start_comment('after test2', indent=4)
+ # EOL is needed here as dedenting gets rid of spaces (as well as does Emacs
+ exp = """
+ xyz:
+ a: 1 # comment 1
+ b: 2
+
+ # xyz
+
+ # before test1 (top level)
+ test1:
+ EOL
+ # before test2
+ test2:
+ # after test2
+ test3: 3
+ """
+ compare_eol(data, exp)
+
+ def test_map_set_comment_before_and_after_non_first_key_02(self) -> None:
+ data = load("""
+ xyz:
+ a: 1 # comment 1
+ b: 2
+
+ test1:
+ test2:
+ test3: 3
+ """)
+ data.yaml_set_comment_before_after_key(
+ 'test1', 'xyz\n\nbefore test1 (top level)', after='\nbefore test2', after_indent=4,
+ )
+ data['test1']['test2'].yaml_set_start_comment('after test2', indent=4)
+ exp = """
+ xyz:
+ a: 1 # comment 1
+ b: 2
+
+ # xyz
+
+ # before test1 (top level)
+ test1:
+
+ # before test2
+ test2:
+ # after test2
+ test3: 3
+ """
+ compare(data, exp)
diff --git a/_test/test_comments.py b/_test/test_comments.py
new file mode 100644
index 0000000..491f00e
--- /dev/null
+++ b/_test/test_comments.py
@@ -0,0 +1,855 @@
+# coding: utf-8
+
+"""
+comment testing is all about roundtrips
+these can be done in the "old" way by creating a file.data and file.roundtrip
+but there is little flexibility in doing that
+
+but some things are not easily tested, eog. how a
+roundtrip changes
+
+"""
+
+import pytest # type: ignore # NOQA
+import sys
+
+from roundtrip import round_trip, dedent, round_trip_load, round_trip_dump # type: ignore
+
+
+class TestComments:
+ def test_no_end_of_file_eol(self) -> None:
+ """not excluding comments caused some problems if at the end of
+ the file without a newline. First error, then included \0 """
+ x = """\
+ - europe: 10 # abc"""
+ round_trip(x, extra='\n')
+ with pytest.raises(AssertionError):
+ round_trip(x, extra='a\n')
+
+ def test_no_comments(self) -> None:
+ round_trip("""
+ - europe: 10
+ - usa:
+ - ohio: 2
+ - california: 9
+ """)
+
+ def test_round_trip_ordering(self) -> None:
+ round_trip("""
+ a: 1
+ b: 2
+ c: 3
+ b1: 2
+ b2: 2
+ d: 4
+ e: 5
+ f: 6
+ """)
+
+ def test_complex(self) -> None:
+ round_trip("""
+ - europe: 10 # top
+ - usa:
+ - ohio: 2
+ - california: 9 # o
+ """)
+
+ def test_dropped(self) -> None:
+ s = """\
+ # comment
+ scalar
+ ...
+ """
+ round_trip(s, 'scalar\n...\n')
+
+ def test_main_mapping_begin_end(self) -> None:
+ round_trip("""
+ # C start a
+ # C start b
+ abc: 1
+ ghi: 2
+ klm: 3
+ # C end a
+ # C end b
+ """)
+
+ def test_reindent(self) -> None:
+ x = """\
+ a:
+ b: # comment 1
+ c: 1 # comment 2
+ """
+ d = round_trip_load(x)
+ y = round_trip_dump(d, indent=4)
+ assert y == dedent("""\
+ a:
+ b: # comment 1
+ c: 1 # comment 2
+ """)
+
+ def test_main_mapping_begin_end_items_post(self) -> None:
+ round_trip("""
+ # C start a
+ # C start b
+ abc: 1 # abc comment
+ ghi: 2
+ klm: 3 # klm comment
+ # C end a
+ # C end b
+ """)
+
+ def test_main_sequence_begin_end(self) -> None:
+ round_trip("""
+ # C start a
+ # C start b
+ - abc
+ - ghi
+ - klm
+ # C end a
+ # C end b
+ """)
+
+ def test_main_sequence_begin_end_items_post(self) -> None:
+ round_trip("""
+ # C start a
+ # C start b
+ - abc # abc comment
+ - ghi
+ - klm # klm comment
+ # C end a
+ # C end b
+ """)
+
+ def test_main_mapping_begin_end_complex(self) -> None:
+ round_trip("""
+ # C start a
+ # C start b
+ abc: 1
+ ghi: 2
+ klm:
+ 3a: alpha
+ 3b: beta # it is all greek to me
+ # C end a
+ # C end b
+ """)
+
+ def test_09(self) -> None: # 2.9 from the examples in the spec
+ s = """\
+ hr: # 1998 hr ranking
+ - Mark McGwire
+ - Sammy Sosa
+ rbi:
+ # 1998 rbi ranking
+ - Sammy Sosa
+ - Ken Griffey
+ """
+ round_trip(s, indent=4, block_seq_indent=2)
+
+ def test_09a(self) -> None:
+ round_trip("""
+ hr: # 1998 hr ranking
+ - Mark McGwire
+ - Sammy Sosa
+ rbi:
+ # 1998 rbi ranking
+ - Sammy Sosa
+ - Ken Griffey
+ """)
+
+ def test_simple_map_middle_comment(self) -> None:
+ round_trip("""
+ abc: 1
+ # C 3a
+ # C 3b
+ ghi: 2
+ """)
+
+ def test_map_in_map_0(self) -> None:
+ round_trip("""
+ map1: # comment 1
+ # comment 2
+ map2:
+ key1: val1
+ """)
+
+ def test_map_in_map_1(self) -> None:
+ # comment is moved from value to key
+ round_trip("""
+ map1:
+ # comment 1
+ map2:
+ key1: val1
+ """)
+
+ def test_application_arguments(self) -> None:
+ # application configur
+ round_trip("""
+ args:
+ username: anthon
+ passwd: secret
+ fullname: Anthon van der Neut
+ tmux:
+ session-name: test
+ loop:
+ wait: 10
+ """)
+
+ def test_substitute(self) -> None:
+ x = """
+ args:
+ username: anthon # name
+ passwd: secret # password
+ fullname: Anthon van der Neut
+ tmux:
+ session-name: test
+ loop:
+ wait: 10
+ """
+ data = round_trip_load(x)
+ data['args']['passwd'] = 'deleted password'
+ # note the requirement to add spaces for alignment of comment
+ x = x.replace(': secret ', ': deleted password')
+ assert round_trip_dump(data) == dedent(x)
+
+ def test_set_comment(self) -> None:
+ round_trip("""
+ !!set
+ # the beginning
+ ? a
+ # next one is B (lowercase)
+ ? b # You see? Promised you.
+ ? c
+ # this is the end
+ """)
+
+ def test_omap_comment_roundtrip(self) -> None:
+ round_trip("""
+ !!omap
+ - a: 1
+ - b: 2 # two
+ - c: 3 # three
+ - d: 4
+ """)
+
+ def test_omap_comment_roundtrip_pre_comment(self) -> None:
+ round_trip("""
+ !!omap
+ - a: 1
+ - b: 2 # two
+ - c: 3 # three
+ # last one
+ - d: 4
+ """)
+
+ def test_non_ascii(self) -> None:
+ round_trip("""
+ verbosity: 1 # 0 is minimal output, -1 none
+ base_url: http://gopher.net
+ special_indices: [1, 5, 8]
+ also_special:
+ - a
+ - 19
+ - 32
+ asia and europe: &asia_europe
+ Turkey: Ankara
+ Russia: Moscow
+ countries:
+ Asia:
+ <<: *asia_europe
+ Japan: Tokyo # 東京
+ Europe:
+ <<: *asia_europe
+ Spain: Madrid
+ Italy: Rome
+ """)
+
+ def test_dump_utf8(self) -> None:
+ import ruamel.yaml # NOQA
+
+ x = dedent("""\
+ ab:
+ - x # comment
+ - y # more comment
+ """)
+ data = round_trip_load(x)
+ for utf in [True, False]:
+ y = round_trip_dump(
+ data, default_flow_style=False, allow_unicode=utf,
+ )
+ assert y == x
+
+ def test_dump_unicode_utf8(self) -> None:
+ import ruamel.yaml # NOQA
+
+ x = dedent("""\
+ ab:
+ - x # comment
+ - y # more comment
+ """)
+ data = round_trip_load(x)
+ for utf in [True, False]:
+ y = round_trip_dump(
+ data, default_flow_style=False, allow_unicode=utf,
+ )
+ assert y == x
+
+ def test_mlget_00(self) -> None:
+ x = """\
+ a:
+ - b:
+ c: 42
+ - d:
+ f: 196
+ e:
+ g: 3.14
+ """
+ d = round_trip_load(x)
+ assert d.mlget(['a', 1, 'd', 'f'], list_ok=True) == 196
+ # with pytest.raises(AssertionError):
+ # d.mlget(['a', 1, 'd', 'f']) == 196
+
+
+class TestInsertPopList:
+ """list insertion is more complex than dict insertion, as you
+ need to move the values to subsequent keys on insert"""
+
+ @property
+ def ins(self) -> str:
+ return """\
+ ab:
+ - a # a
+ - b # b
+ - c
+ - d # d
+
+ de:
+ - 1
+ - 2
+ """
+
+ def test_insert_0(self) -> None:
+ d = round_trip_load(self.ins)
+ d['ab'].insert(0, 'xyz')
+ y = round_trip_dump(d, indent=2)
+ assert y == dedent("""\
+ ab:
+ - xyz
+ - a # a
+ - b # b
+ - c
+ - d # d
+
+ de:
+ - 1
+ - 2
+ """)
+
+ def test_insert_1(self) -> None:
+ d = round_trip_load(self.ins)
+ d['ab'].insert(4, 'xyz')
+ y = round_trip_dump(d, indent=2)
+ assert y == dedent("""\
+ ab:
+ - a # a
+ - b # b
+ - c
+ - d # d
+
+ - xyz
+ de:
+ - 1
+ - 2
+ """)
+
+ def test_insert_2(self) -> None:
+ d = round_trip_load(self.ins)
+ d['ab'].insert(1, 'xyz')
+ y = round_trip_dump(d, indent=2)
+ assert y == dedent("""\
+ ab:
+ - a # a
+ - xyz
+ - b # b
+ - c
+ - d # d
+
+ de:
+ - 1
+ - 2
+ """)
+
+ def test_pop_0(self) -> None:
+ d = round_trip_load(self.ins)
+ d['ab'].pop(0)
+ y = round_trip_dump(d, indent=2)
+ print(y)
+ assert y == dedent("""\
+ ab:
+ - b # b
+ - c
+ - d # d
+
+ de:
+ - 1
+ - 2
+ """)
+
+ def test_pop_1(self) -> None:
+ d = round_trip_load(self.ins)
+ d['ab'].pop(1)
+ y = round_trip_dump(d, indent=2)
+ print(y)
+ assert y == dedent("""\
+ ab:
+ - a # a
+ - c
+ - d # d
+
+ de:
+ - 1
+ - 2
+ """)
+
+ def test_pop_2(self) -> None:
+ d = round_trip_load(self.ins)
+ d['ab'].pop(2)
+ y = round_trip_dump(d, indent=2)
+ print(y)
+ assert y == dedent("""\
+ ab:
+ - a # a
+ - b # b
+ - d # d
+
+ de:
+ - 1
+ - 2
+ """)
+
+ def test_pop_3(self) -> None:
+ d = round_trip_load(self.ins)
+ d['ab'].pop(3)
+ y = round_trip_dump(d, indent=2)
+ print(y)
+ assert y == dedent("""\
+ ab:
+ - a # a
+ - b # b
+ - c
+ de:
+ - 1
+ - 2
+ """)
+
+
+# inspired by demux' question on stackoverflow
+# http://stackoverflow.com/a/36970608/1307905
+class TestInsertInMapping:
+ @property
+ def ins(self) -> str:
+ return """\
+ first_name: Art
+ occupation: Architect # This is an occupation comment
+ about: Art Vandelay is a fictional character that George invents...
+ """
+
+ def test_insert_at_pos_1(self) -> None:
+ d = round_trip_load(self.ins)
+ d.insert(1, 'last name', 'Vandelay', comment='new key')
+ y = round_trip_dump(d)
+ print(y)
+ assert y == dedent("""\
+ first_name: Art
+ last name: Vandelay # new key
+ occupation: Architect # This is an occupation comment
+ about: Art Vandelay is a fictional character that George invents...
+ """)
+
+ def test_insert_at_pos_0(self) -> None:
+ d = round_trip_load(self.ins)
+ d.insert(0, 'last name', 'Vandelay', comment='new key')
+ y = round_trip_dump(d)
+ print(y)
+ assert y == dedent("""\
+ last name: Vandelay # new key
+ first_name: Art
+ occupation: Architect # This is an occupation comment
+ about: Art Vandelay is a fictional character that George invents...
+ """)
+
+ def test_insert_at_pos_3(self) -> None:
+ # much more simple if done with appending.
+ d = round_trip_load(self.ins)
+ d.insert(3, 'last name', 'Vandelay', comment='new key')
+ y = round_trip_dump(d)
+ print(y)
+ assert y == dedent("""\
+ first_name: Art
+ occupation: Architect # This is an occupation comment
+ about: Art Vandelay is a fictional character that George invents...
+ last name: Vandelay # new key
+ """)
+
+
+class TestCommentedMapMerge:
+ def test_in_operator(self) -> None:
+ data = round_trip_load("""
+ x: &base
+ a: 1
+ b: 2
+ c: 3
+ y:
+ <<: *base
+ k: 4
+ l: 5
+ """)
+ assert data['x']['a'] == 1
+ assert 'a' in data['x']
+ assert data['y']['a'] == 1
+ assert 'a' in data['y']
+
+ def test_issue_60(self) -> None:
+ data = round_trip_load("""
+ x: &base
+ a: 1
+ y:
+ <<: *base
+ """)
+ assert data['x']['a'] == 1
+ assert data['y']['a'] == 1
+ assert str(data['y']) == """{'a': 1}"""
+
+ def test_issue_60_1(self) -> None:
+ data = round_trip_load("""
+ x: &base
+ a: 1
+ y:
+ <<: *base
+ b: 2
+ """)
+ assert data['x']['a'] == 1
+ assert data['y']['a'] == 1
+ assert str(data['y']) == """{'b': 2, 'a': 1}"""
+
+
+class TestEmptyLines:
+ # prompted by issue 46 from Alex Harvey
+ def test_issue_46(self) -> None:
+ yaml_str = dedent("""\
+ ---
+ # Please add key/value pairs in alphabetical order
+
+ aws_s3_bucket: 'mys3bucket'
+
+ jenkins_ad_credentials:
+ bind_name: 'CN=svc-AAA-BBB-T,OU=Example,DC=COM,DC=EXAMPLE,DC=Local'
+ bind_pass: 'xxxxyyyy{'
+ """)
+ d = round_trip_load(yaml_str, preserve_quotes=True)
+ y = round_trip_dump(d, explicit_start=True)
+ assert yaml_str == y
+
+ def test_multispace_map(self) -> None:
+ round_trip("""
+ a: 1x
+
+ b: 2x
+
+
+ c: 3x
+
+
+
+ d: 4x
+
+ """)
+
+ @pytest.mark.xfail(strict=True) # type: ignore
+ def test_multispace_map_initial(self) -> None:
+ round_trip("""
+
+ a: 1x
+
+ b: 2x
+
+
+ c: 3x
+
+
+
+ d: 4x
+
+ """)
+
+ def test_embedded_map(self) -> None:
+ round_trip("""
+ - a: 1y
+ b: 2y
+
+ c: 3y
+ """)
+
+ def test_toplevel_seq(self) -> None:
+ round_trip("""\
+ - 1
+
+ - 2
+
+ - 3
+ """)
+
+ def test_embedded_seq(self) -> None:
+ round_trip("""
+ a:
+ b:
+ - 1
+
+ - 2
+
+
+ - 3
+ """)
+
+ def test_line_with_only_spaces(self) -> None:
+ # issue 54
+ yaml_str = "---\n\na: 'x'\n \nb: y\n"
+ d = round_trip_load(yaml_str, preserve_quotes=True)
+ y = round_trip_dump(d, explicit_start=True)
+ stripped = ""
+ for line in yaml_str.splitlines():
+ stripped += line.rstrip() + '\n'
+ print(line + '$')
+ assert stripped == y
+
+ def test_some_eol_spaces(self) -> None:
+ # spaces after tokens and on empty lines
+ yaml_str = '--- \n \na: "x" \n \nb: y \n'
+ d = round_trip_load(yaml_str, preserve_quotes=True)
+ y = round_trip_dump(d, explicit_start=True)
+ stripped = ""
+ for line in yaml_str.splitlines():
+ stripped += line.rstrip() + '\n'
+ print(line + '$')
+ assert stripped == y
+
+ def test_issue_54_not_ok(self) -> None:
+ yaml_str = dedent("""\
+ toplevel:
+
+ # some comment
+ sublevel: 300
+ """)
+ d = round_trip_load(yaml_str)
+ print(d.ca)
+ y = round_trip_dump(d, indent=4)
+ assert isinstance(y, str)
+ print(y.replace('\n', '$\n'))
+ assert yaml_str == y
+
+ def test_issue_54_ok(self) -> None:
+ yaml_str = dedent("""\
+ toplevel:
+ # some comment
+ sublevel: 300
+ """)
+ d = round_trip_load(yaml_str)
+ y = round_trip_dump(d, indent=4)
+ assert yaml_str == y
+
+ def test_issue_93(self) -> None:
+ round_trip("""\
+ a:
+ b:
+ - c1: cat # a1
+ # my comment on catfish
+ - c2: catfish # a2
+ """)
+
+ def test_issue_93_00(self) -> None:
+ round_trip("""\
+ a:
+ - - c1: cat # a1
+ # my comment on catfish
+ - c2: catfish # a2
+ """)
+
+ def test_issue_93_01(self) -> None:
+ round_trip("""\
+ - - c1: cat # a1
+ # my comment on catfish
+ - c2: catfish # a2
+ """)
+
+ def test_issue_93_02(self) -> None:
+ # never failed as there is no indent
+ round_trip("""\
+ - c1: cat
+ # my comment on catfish
+ - c2: catfish
+ """)
+
+ def test_issue_96(self) -> None:
+ # inserted extra line on trailing spaces
+ round_trip("""\
+ a:
+ b:
+ c: c_val
+ d:
+
+ e:
+ g: g_val
+ """)
+
+
+class TestUnicodeComments:
+ @pytest.mark.skipif(sys.version_info < (2, 7), reason='wide unicode') # type: ignore
+ def test_issue_55(self) -> None: # reported by Haraguroicha Hsu
+ round_trip("""\
+ name: TEST
+ description: test using
+ author: Harguroicha
+ sql:
+ command: |-
+ select name from testtbl where no = :no
+
+ ci-test:
+ - :no: 04043709 # 小花
+ - :no: 05161690 # 茶
+ - :no: 05293147 # 〇𤋥川
+ - :no: 05338777 # 〇〇啓
+ - :no: 05273867 # 〇
+ - :no: 05205786 # 〇𤦌
+ """)
+
+
+class TestEmptyValueBeforeComments:
+ def test_issue_25a(self) -> None:
+ round_trip("""\
+ - a: b
+ c: d
+ d: # foo
+ - e: f
+ """)
+
+ def test_issue_25a1(self) -> None:
+ round_trip("""\
+ - a: b
+ c: d
+ d: # foo
+ e: f
+ """)
+
+ def test_issue_25b(self) -> None:
+ round_trip("""\
+ var1: #empty
+ var2: something #notempty
+ """)
+
+ def test_issue_25c(self) -> None:
+ round_trip("""\
+ params:
+ a: 1 # comment a
+ b: # comment b
+ c: 3 # comment c
+ """)
+
+ def test_issue_25c1(self) -> None:
+ round_trip("""\
+ params:
+ a: 1 # comment a
+ b: # comment b
+ # extra
+ c: 3 # comment c
+ """)
+
+ def test_issue_25_00(self) -> None:
+ round_trip("""\
+ params:
+ a: 1 # comment a
+ b: # comment b
+ """)
+
+ def test_issue_25_01(self) -> None:
+ round_trip("""\
+ a: # comment 1
+ # comment 2
+ - b: # comment 3
+ c: 1 # comment 4
+ """)
+
+ def test_issue_25_02(self) -> None:
+ round_trip("""\
+ a: # comment 1
+ # comment 2
+ - b: 2 # comment 3
+ """)
+
+ def test_issue_25_03(self) -> None:
+ s = """\
+ a: # comment 1
+ # comment 2
+ - b: 2 # comment 3
+ """
+ round_trip(s, indent=4, block_seq_indent=2)
+
+ def test_issue_25_04(self) -> None:
+ round_trip("""\
+ a: # comment 1
+ # comment 2
+ b: 1 # comment 3
+ """)
+
+ def test_flow_seq_within_seq(self) -> None:
+ round_trip("""\
+ # comment 1
+ - a
+ - b
+ # comment 2
+ - c
+ - d
+ # comment 3
+ - [e]
+ - f
+ # comment 4
+ - []
+ """)
+
+ def test_comment_after_block_scalar_indicator(self) -> None:
+ round_trip("""\
+ a: | # abc
+ test 1
+ test 2
+ # all done
+ """)
+
+
+test_block_scalar_commented_line_template = """\
+y: p
+# Some comment
+
+a: |
+ x
+{}b: y
+"""
+
+
+class TestBlockScalarWithComments:
+ # issue 99 reported by Colm O'Connor
+ def test_scalar_with_comments(self) -> None:
+ import ruamel.yaml # NOQA
+
+ for x in [
+ "",
+ '\n',
+ '\n# Another comment\n',
+ '\n\n',
+ '\n\n# abc\n#xyz\n',
+ '\n\n# abc\n#xyz\n',
+ '# abc\n\n#xyz\n',
+ '\n\n # abc\n #xyz\n',
+ ]:
+
+ commented_line = test_block_scalar_commented_line_template.format(x)
+ data = round_trip_load(commented_line)
+
+ assert round_trip_dump(data) == commented_line
diff --git a/_test/test_contextmanager.py b/_test/test_contextmanager.py
new file mode 100644
index 0000000..e6256d3
--- /dev/null
+++ b/_test/test_contextmanager.py
@@ -0,0 +1,117 @@
+# coding: utf-8
+
+"""
+testing of anchors and the aliases referring to them
+"""
+
+import sys
+import pytest # type: ignore
+
+from typing import Any
+
+single_doc = """\
+- a: 1
+- b:
+ - 2
+ - 3
+"""
+
+single_data = [dict(a=1), dict(b=[2, 3])]
+
+multi_doc = """\
+---
+- abc
+- xyz
+---
+- a: 1
+- b:
+ - 2
+ - 3
+"""
+
+multi_doc_data = [['abc', 'xyz'], single_data]
+
+
+def get_yaml() -> Any:
+ from ruamel.yaml import YAML
+
+ return YAML()
+
+
+class TestOldStyle:
+ def test_single_load(self) -> None:
+ d = get_yaml().load(single_doc)
+ print(d)
+ print(type(d[0]))
+ assert d == single_data
+
+ def test_single_load_no_arg(self) -> None:
+ with pytest.raises(TypeError):
+ assert get_yaml().load() == single_data
+
+ def test_multi_load(self) -> None:
+ data = list(get_yaml().load_all(multi_doc))
+ assert data == multi_doc_data
+
+ def test_single_dump(self, capsys: Any) -> None:
+ get_yaml().dump(single_data, sys.stdout)
+ out, err = capsys.readouterr()
+ assert out == single_doc
+
+ def test_multi_dump(self, capsys: Any) -> None:
+ yaml = get_yaml()
+ yaml.explicit_start = True
+ yaml.dump_all(multi_doc_data, sys.stdout)
+ out, err = capsys.readouterr()
+ assert out == multi_doc
+
+
+class TestContextManager:
+ def test_single_dump(self, capsys: Any) -> None:
+ from ruamel.yaml import YAML
+
+ with YAML(output=sys.stdout) as yaml:
+ yaml.dump(single_data)
+ out, err = capsys.readouterr()
+ print(err)
+ assert out == single_doc
+
+ def test_multi_dump(self, capsys: Any) -> None:
+ from ruamel.yaml import YAML
+
+ with YAML(output=sys.stdout) as yaml:
+ yaml.explicit_start = True
+ yaml.dump(multi_doc_data[0])
+ yaml.dump(multi_doc_data[1])
+
+ out, err = capsys.readouterr()
+ print(err)
+ assert out == multi_doc
+
+ # input is not as simple with a context manager
+ # you need to indicate what you expect hence load and load_all
+
+ # @pytest.mark.xfail(strict=True)
+ # def test_single_load(self):
+ # from ruamel.yaml import YAML
+ # with YAML(input=single_doc) as yaml:
+ # assert yaml.load() == single_data
+ #
+ # @pytest.mark.xfail(strict=True)
+ # def test_multi_load(self):
+ # from ruamel.yaml import YAML
+ # with YAML(input=multi_doc) as yaml:
+ # for idx, data in enumerate(yaml.load()):
+ # assert data == multi_doc_data[0]
+
+ def test_roundtrip(self, capsys: Any) -> None:
+ from ruamel.yaml import YAML
+
+ with YAML(output=sys.stdout) as yaml:
+ yaml.explicit_start = True
+ for data in yaml.load_all(multi_doc):
+ yaml.dump(data)
+
+ out, err = capsys.readouterr()
+ print(err)
+ assert out == multi_doc
diff --git a/_test/test_copy.py b/_test/test_copy.py
new file mode 100644
index 0000000..ef37d6d
--- /dev/null
+++ b/_test/test_copy.py
@@ -0,0 +1,117 @@
+# coding: utf-8
+
+"""
+Testing copy and deepcopy, instigated by Issue 84 (Peter Amstutz)
+"""
+
+import copy
+
+import pytest # type: ignore # NOQA
+
+from roundtrip import dedent, round_trip_load, round_trip_dump # type: ignore
+
+
+class TestDeepCopy:
+ def test_preserve_flow_style_simple(self) -> None:
+ x = dedent("""\
+ {foo: bar, baz: quux}
+ """)
+ data = round_trip_load(x)
+ data_copy = copy.deepcopy(data)
+ y = round_trip_dump(data_copy)
+ print('x [{}]'.format(x))
+ print('y [{}]'.format(y))
+ assert y == x
+ assert data.fa.flow_style() == data_copy.fa.flow_style()
+
+ def test_deepcopy_flow_style_nested_dict(self) -> None:
+ x = dedent("""\
+ a: {foo: bar, baz: quux}
+ """)
+ data = round_trip_load(x)
+ assert data['a'].fa.flow_style() is True
+ data_copy = copy.deepcopy(data)
+ assert data_copy['a'].fa.flow_style() is True
+ data_copy['a'].fa.set_block_style()
+ assert data['a'].fa.flow_style() != data_copy['a'].fa.flow_style()
+ assert data['a'].fa._flow_style is True
+ assert data_copy['a'].fa._flow_style is False
+ y = round_trip_dump(data_copy)
+
+ print('x [{}]'.format(x))
+ print('y [{}]'.format(y))
+ assert y == dedent("""\
+ a:
+ foo: bar
+ baz: quux
+ """)
+
+ def test_deepcopy_flow_style_nested_list(self) -> None:
+ x = dedent("""\
+ a: [1, 2, 3]
+ """)
+ data = round_trip_load(x)
+ assert data['a'].fa.flow_style() is True
+ data_copy = copy.deepcopy(data)
+ assert data_copy['a'].fa.flow_style() is True
+ data_copy['a'].fa.set_block_style()
+ assert data['a'].fa.flow_style() != data_copy['a'].fa.flow_style()
+ assert data['a'].fa._flow_style is True
+ assert data_copy['a'].fa._flow_style is False
+ y = round_trip_dump(data_copy)
+
+ print('x [{}]'.format(x))
+ print('y [{}]'.format(y))
+ assert y == dedent("""\
+ a:
+ - 1
+ - 2
+ - 3
+ """)
+
+
+class TestCopy:
+ def test_copy_flow_style_nested_dict(self) -> None:
+ x = dedent("""\
+ a: {foo: bar, baz: quux}
+ """)
+ data = round_trip_load(x)
+ assert data['a'].fa.flow_style() is True
+ data_copy = copy.copy(data)
+ assert data_copy['a'].fa.flow_style() is True
+ data_copy['a'].fa.set_block_style()
+ assert data['a'].fa.flow_style() == data_copy['a'].fa.flow_style()
+ assert data['a'].fa._flow_style is False
+ assert data_copy['a'].fa._flow_style is False
+ y = round_trip_dump(data_copy)
+ z = round_trip_dump(data)
+ assert y == z
+
+ assert y == dedent("""\
+ a:
+ foo: bar
+ baz: quux
+ """)
+
+ def test_copy_flow_style_nested_list(self) -> None:
+ x = dedent("""\
+ a: [1, 2, 3]
+ """)
+ data = round_trip_load(x)
+ assert data['a'].fa.flow_style() is True
+ data_copy = copy.copy(data)
+ assert data_copy['a'].fa.flow_style() is True
+ data_copy['a'].fa.set_block_style()
+ assert data['a'].fa.flow_style() == data_copy['a'].fa.flow_style()
+ assert data['a'].fa._flow_style is False
+ assert data_copy['a'].fa._flow_style is False
+ y = round_trip_dump(data_copy)
+
+ print('x [{}]'.format(x))
+ print('y [{}]'.format(y))
+ assert y == dedent("""\
+ a:
+ - 1
+ - 2
+ - 3
+ """)
diff --git a/_test/test_cyaml.py b/_test/test_cyaml.py
new file mode 100644
index 0000000..ce6a543
--- /dev/null
+++ b/_test/test_cyaml.py
@@ -0,0 +1,86 @@
+# coding: utf-8
+
+import sys
+import platform
+import pytest # type: ignore # NOQA
+from textwrap import dedent
+
+NO_CLIB_VER = (3, 12)
+
+
+@pytest.mark.skipif( # type: ignore
+ platform.python_implementation() in ['Jython', 'PyPy'],
+ reason='Jython throws RepresenterError',
+)
+def test_load_cyaml() -> None:
+ print("???????????????????????", platform.python_implementation())
+ import ruamel.yaml
+
+ if sys.version_info >= NO_CLIB_VER:
+ return
+ yaml = ruamel.yaml.YAML(typ='safe', pure=False)
+ assert ruamel.yaml.__with_libyaml__
+
+ yaml.load('abc: 1')
+
+
+@pytest.mark.skipif(sys.version_info >= NO_CLIB_VER # type: ignore
+ or platform.python_implementation() in ['Jython', 'PyPy'],
+ reason='no _PyGC_FINALIZED')
+def test_dump_cyaml() -> None:
+ import ruamel.yaml
+
+ if sys.version_info >= NO_CLIB_VER:
+ return
+ data = {'a': 1, 'b': 2}
+ yaml = ruamel.yaml.YAML(typ='safe', pure=False)
+ yaml.default_flow_style = False
+ yaml.allow_unicode = True
+ buf = ruamel.yaml.compat.StringIO()
+ yaml.dump(data, buf)
+ assert buf.getvalue() == 'a: 1\nb: 2\n'
+
+
+@pytest.mark.skipif( # type: ignore
+ platform.python_implementation() in ['Jython', 'PyPy'], reason='not avialable',
+)
+def test_load_cyaml_1_2() -> None:
+ # issue 155
+ import ruamel.yaml
+
+ if sys.version_info >= NO_CLIB_VER:
+ return
+ assert ruamel.yaml.__with_libyaml__
+ inp = dedent("""\
+ %YAML 1.2
+ ---
+ num_epochs: 70000
+ """)
+ yaml = ruamel.yaml.YAML(typ='safe')
+ yaml.load(inp)
+
+
+@pytest.mark.skipif( # type: ignore
+ platform.python_implementation() in ['Jython', 'PyPy'], reason='not available',
+)
+def test_dump_cyaml_1_2() -> None:
+ # issue 155
+ import ruamel.yaml
+ from ruamel.yaml.compat import StringIO
+
+ if sys.version_info >= NO_CLIB_VER:
+ return
+ assert ruamel.yaml.__with_libyaml__
+ yaml = ruamel.yaml.YAML(typ='safe')
+ yaml.version = (1, 2)
+ yaml.default_flow_style = False
+ data = {'a': 1, 'b': 2}
+ exp = dedent("""\
+ %YAML 1.2
+ ---
+ a: 1
+ b: 2
+ """)
+ buf = StringIO()
+ yaml.dump(data, buf)
+ assert buf.getvalue() == exp
diff --git a/_test/test_dataclass.py b/_test/test_dataclass.py
new file mode 100644
index 0000000..bcce1ba
--- /dev/null
+++ b/_test/test_dataclass.py
@@ -0,0 +1,180 @@
+
+from __future__ import annotations
+
+from dataclasses import dataclass, fields, InitVar # NOQA
+from textwrap import dedent
+from io import BytesIO
+from typing import ClassVar, Union
+
+
+class TestDataClasses:
+ def test_1(self) -> None:
+ from ruamel.yaml import YAML
+
+ yaml = YAML()
+
+ @yaml.register_class
+ @dataclass
+ class DC:
+ abc: int
+ klm: int
+ xyz: int = 0
+
+ def __post_init__(self) -> None:
+ self.xyz = self.abc + self.klm
+
+ dc = DC(abc=5, klm=42)
+ assert dc.xyz == 47
+
+ yaml_str = dedent("""\
+ !DC
+ abc: 13
+ klm: 37
+ """)
+ dc2 = yaml.load(yaml_str)
+ assert dc2.xyz == 50
+
+ def test_yamltag(self) -> None:
+ from ruamel.yaml import YAML
+
+ yaml = YAML()
+
+ @yaml.register_class
+ @dataclass
+ class DC:
+ yaml_tag: ClassVar = '!dc_example'
+ abc: int
+ klm: int
+
+ dc = DC(abc=5, klm=42)
+ buf = BytesIO()
+ yaml.dump(dc, buf)
+ assert buf.getvalue() == dedent("""\
+ !dc_example
+ abc: 5
+ klm: 42
+ """).encode('utf-8')
+ dc2 = yaml.load(buf.getvalue())
+ assert len(fields(dc2)) == 2 # class var is not a field
+ assert dc2.abc == dc.abc
+ assert dc2.klm == dc.klm
+
+ def test_initvar(self) -> None:
+ from ruamel.yaml import YAML
+
+ yaml = YAML()
+
+ @yaml.register_class
+ @dataclass
+ class DC:
+ abc: int
+ klm: int
+ xyz: InitVar[Union[str, None]] = None
+
+ def __post_init__(self, xyz: Union[str, None]) -> None:
+ # assert xyz == self.xyz # self.xyz is always None
+ if xyz is not None:
+ self.klm += len(xyz)
+
+ dc = DC(abc=5, klm=42, xyz='provided')
+ # this actually doesn't raise an attribute error, I would have expected it not to work
+ # at all, but it has the default value
+ assert dc.xyz is None # type: ignore
+ buf = BytesIO()
+ yaml.dump(dc, buf)
+ assert buf.getvalue() == dedent("""\
+ !DC
+ abc: 5
+ klm: 50
+ """).encode('utf-8')
+
+ yaml_str = dedent("""\
+ !DC
+ abc: 18
+ klm: 55
+ xyz: some string
+ """)
+ dc2 = yaml.load(yaml_str)
+ assert dc2.xyz is None
+ assert dc2.klm == 55 + len('some string')
+
+ def test_initvar_not_in_yaml(self) -> None:
+ from ruamel.yaml import YAML
+
+ yaml = YAML()
+
+ @yaml.register_class
+ @dataclass
+ class DC:
+ abc: int
+ klm: int
+ xyz: InitVar[Union[str, None]] = 'hello'
+
+ def __post_init__(self, xyz: Union[str, None]) -> None:
+ # assert xyz == self.xyz # self.xyz is always None
+ if xyz is not None:
+ self.klm += len(xyz)
+
+ dc = DC(abc=5, klm=42, xyz='provided')
+ assert dc.abc == 5
+ assert dc.xyz == 'hello' # type: ignore
+ buf = BytesIO()
+ yaml.dump(dc, buf)
+ assert buf.getvalue() == dedent("""\
+ !DC
+ abc: 5
+ klm: 50
+ """).encode('utf-8')
+
+ yaml_str = dedent("""\
+ !DC
+ abc: 18
+ klm: 55
+ """)
+ dc2 = yaml.load(yaml_str)
+ assert dc2.xyz == 'hello'
+ assert dc2.klm == 55 + len('hello')
+
+ def test_collection_field(self) -> None:
+ # https://stackoverflow.com/a/77485786/1307905
+ import ruamel.yaml
+ from dataclasses import dataclass
+
+ @dataclass
+ class Msg:
+
+ id: int
+ desc: str
+ fields: list[Field]
+
+ def __post_init__(self) -> None:
+ idx: int = 0
+ for field in self.fields: # why is this empty??
+ field.index = idx
+ idx += field.size
+
+ @dataclass
+ class Field:
+ id: int
+ name: str
+ units: str
+ size: int
+ index: int = -1
+
+ yaml = ruamel.yaml.YAML()
+ yaml.register_class(Msg)
+ yaml.register_class(Field)
+
+ msg: Msg = yaml.load("""\
+ !Msg
+ id: 1
+ desc: status
+ fields:
+ - !Field
+ id: 1
+ name: Temp
+ units: degC
+ size: 2
+ """)
+
+ assert msg.fields[0].index != -1
diff --git a/_test/test_datetime.py b/_test/test_datetime.py
new file mode 100644
index 0000000..b68d8e5
--- /dev/null
+++ b/_test/test_datetime.py
@@ -0,0 +1,193 @@
+# coding: utf-8
+
+"""
+http://yaml.org/type/timestamp.html specifies the regexp to use
+for datetime.date and datetime.datetime construction. Date is simple
+but datetime can have 'T' or 't' as well as 'Z' or a timezone offset (in
+hours and minutes). This information was originally used to create
+a UTC datetime and then discarded
+
+examples from the above:
+
+canonical: 2001-12-15T02:59:43.1Z
+valid iso8601: 2001-12-14t21:59:43.10-05:00
+space separated: 2001-12-14 21:59:43.10 -5
+no time zone (Z): 2001-12-15 2:59:43.10
+date (00:00:00Z): 2002-12-14
+
+Please note that a fraction can only be included if not equal to 0
+
+"""
+
+import sys
+import copy
+import pytest # type: ignore # NOQA
+from datetime import datetime as DateTime, timezone as TimeZone, timedelta as TimeDelta
+
+from roundtrip import round_trip, dedent, round_trip_load, round_trip_dump # type: ignore # NOQA
+
+
+class TestDateTime:
+ def test_date_only(self) -> None:
+ inp = """
+ - 2011-10-02
+ """
+ exp = """
+ - 2011-10-02
+ """
+ round_trip(inp, exp)
+
+ def test_zero_fraction(self) -> None:
+ inp = """
+ - 2011-10-02 16:45:00.0
+ """
+ exp = """
+ - 2011-10-02 16:45:00
+ """
+ round_trip(inp, exp)
+
+ def test_long_fraction(self) -> None:
+ inp = """
+ - 2011-10-02 16:45:00.1234 # expand with zeros
+ - 2011-10-02 16:45:00.123456
+ - 2011-10-02 16:45:00.12345612 # round to microseconds
+ - 2011-10-02 16:45:00.1234565 # round up
+ - 2011-10-02 16:45:00.12345678 # round up
+ """
+ exp = """
+ - 2011-10-02 16:45:00.123400 # expand with zeros
+ - 2011-10-02 16:45:00.123456
+ - 2011-10-02 16:45:00.123456 # round to microseconds
+ - 2011-10-02 16:45:00.123457 # round up
+ - 2011-10-02 16:45:00.123457 # round up
+ """
+ round_trip(inp, exp)
+
+ def test_canonical(self) -> None:
+ inp = """
+ - 2011-10-02T16:45:00.1Z
+ """
+ exp = """
+ - 2011-10-02T16:45:00.100000Z
+ """
+ round_trip(inp, exp)
+
+ def test_spaced_timezone(self) -> None:
+ inp = """
+ - 2011-10-02T11:45:00 -5
+ """
+ exp = """
+ - 2011-10-02T11:45:00-5
+ """
+ round_trip(inp, exp)
+
+ def test_normal_timezone(self) -> None:
+ round_trip("""
+ - 2011-10-02T11:45:00-5
+ - 2011-10-02 11:45:00-5
+ - 2011-10-02T11:45:00-05:00
+ - 2011-10-02 11:45:00-05:00
+ """)
+
+ def test_no_timezone(self) -> None:
+ inp = """
+ - 2011-10-02 6:45:00
+ """
+ exp = """
+ - 2011-10-02 06:45:00
+ """
+ round_trip(inp, exp)
+
+ def test_explicit_T(self) -> None:
+ inp = """
+ - 2011-10-02T16:45:00
+ """
+ exp = """
+ - 2011-10-02T16:45:00
+ """
+ round_trip(inp, exp)
+
+ def test_explicit_t(self) -> None: # to upper
+ inp = """
+ - 2011-10-02t16:45:00
+ """
+ exp = """
+ - 2011-10-02T16:45:00
+ """
+ round_trip(inp, exp)
+
+ def test_no_T_multi_space(self) -> None:
+ inp = """
+ - 2011-10-02 16:45:00
+ """
+ exp = """
+ - 2011-10-02 16:45:00
+ """
+ round_trip(inp, exp)
+
+ def test_iso(self) -> None:
+ round_trip("""
+ - 2011-10-02T15:45:00+01:00
+ """)
+
+ def test_zero_tz(self) -> None:
+ round_trip("""
+ - 2011-10-02T15:45:00+0
+ """)
+
+ def test_issue_45(self) -> None:
+ round_trip("""
+ dt: 2016-08-19T22:45:47Z
+ """)
+
+ def test_issue_366(self) -> None:
+ import ruamel.yaml
+ import io
+
+ round_trip("""
+ [2021-02-01 22:34:48.696868-03:00]
+ """)
+ yaml = ruamel.yaml.YAML()
+ dd = DateTime(2021, 2, 1, 22, 34, 48, 696868, TimeZone(TimeDelta(hours=-3), name=''))
+ buf = io.StringIO()
+ yaml.dump(dd, buf)
+ assert buf.getvalue() == '2021-02-01 22:34:48.696868-03:00\n...\n'
+ rd = yaml.load(buf.getvalue())
+ assert rd == dd
+
+ def test_deepcopy_datestring(self) -> None:
+ # reported by Quuxplusone, http://stackoverflow.com/a/41577841/1307905
+ x = dedent("""\
+ foo: 2016-10-12T12:34:56
+ """)
+ data = copy.deepcopy(round_trip_load(x))
+ assert round_trip_dump(data) == x
+
+ def test_fraction_overflow(self) -> None:
+ # reported (indirectly) by Luís Ferreira
+ # https://sourceforge.net/p/ruamel-yaml/tickets/414/
+ inp = dedent("""\
+ - 2022-01-02T12:34:59.9999994
+ - 2022-01-02T12:34:59.9999995
+ """)
+ exp = dedent("""\
+ - 2022-01-02T12:34:59.999999
+ - 2022-01-02T12:35:00
+ """)
+ round_trip(inp, exp)
+
+ def Xtest_tzinfo(self) -> None:
+ import ruamel.yaml
+
+ yaml = ruamel.yaml.YAML()
+ dts = '2011-10-02T16:45:00.930619+01:00'
+ d = yaml.load(dts)
+ print('d', repr(d), d._yaml)
+ yaml.dump(dict(x=d), sys.stdout)
+ print('----')
+ # dx = DateTime.fromisoformat(dts)
+ # print('dx', dx, repr(dx))
+ dd = DateTime(2011, 10, 2, 16, 45, 00, 930619, TimeZone(TimeDelta(hours=1, minutes=0), name='+01:00')) # NOQA
+ yaml.dump([dd], sys.stdout)
+ print('dd', dd, dd.tzinfo)
+ raise AssertionError()
diff --git a/_test/test_deprecation.py b/_test/test_deprecation.py
new file mode 100644
index 0000000..ea86f5f
--- /dev/null
+++ b/_test/test_deprecation.py
@@ -0,0 +1,129 @@
+# coding: utf-8
+
+import sys
+import pytest # type:ignore # NOQA
+
+last_to_warn = (0, 17, 40)
+
+
+@pytest.mark.skipif(sys.version_info < (3, 7) or sys.version_info >= (3, 9), # type: ignore
+ reason='collections not available?')
+def test_collections_deprecation() -> None:
+ with pytest.warns(DeprecationWarning):
+ from collections import Hashable # type: ignore # NOQA
+
+
+class TestFunctionDeprecation:
+ def test_deprecation_scan(self) -> None:
+ import ruamel.yaml
+
+ if ruamel.yaml.version_info <= last_to_warn:
+ with pytest.warns(PendingDeprecationWarning):
+ data = ruamel.yaml.load('a: 42') # NOQA
+ else:
+ with pytest.raises(AttributeError):
+ data = ruamel.yaml.load('a: 42') # NOQA
+
+ def test_deprecation_parse(self) -> None:
+ import ruamel.yaml
+
+ if ruamel.yaml.version_info <= last_to_warn:
+ data = ruamel.yaml.parse('a: 42') # NOQA
+ else:
+ with pytest.raises(AttributeError):
+ data = ruamel.yaml.parse('a: 42') # NOQA
+
+ def test_deprecation_compose(self) -> None:
+ import ruamel.yaml
+
+ if ruamel.yaml.version_info <= last_to_warn:
+ with pytest.warns(PendingDeprecationWarning):
+ data = ruamel.yaml.compose('a: 42') # NOQA
+ else:
+ with pytest.raises(AttributeError):
+ data = ruamel.yaml.parse('a: 42') # NOQA
+
+ def test_deprecation_compose_all(self) -> None:
+ import ruamel.yaml
+
+ if ruamel.yaml.version_info <= last_to_warn:
+ data = ruamel.yaml.compose_all('a: 42') # NOQA
+ else:
+ with pytest.raises(AttributeError):
+ data = ruamel.yaml.parse('a: 42') # NOQA
+
+ def test_deprecation_load(self) -> None:
+ import ruamel.yaml
+
+ if ruamel.yaml.version_info <= last_to_warn:
+ with pytest.warns(PendingDeprecationWarning):
+ data = ruamel.yaml.load('a: 42') # NOQA
+ else:
+ with pytest.raises(AttributeError):
+ data = ruamel.yaml.parse('a: 42') # NOQA
+
+ def test_deprecation_load_all(self) -> None:
+ import ruamel.yaml
+
+ if ruamel.yaml.version_info <= last_to_warn:
+ data = ruamel.yaml.load_all('a: 42') # NOQA
+ else:
+ with pytest.raises(AttributeError):
+ data = ruamel.yaml.parse('a: 42') # NOQA
+
+ def test_deprecation_safe_load(self) -> None:
+ import ruamel.yaml
+
+ if ruamel.yaml.version_info <= last_to_warn:
+ with pytest.warns(PendingDeprecationWarning):
+ data = ruamel.yaml.safe_load('a: 42') # NOQA
+ else:
+ with pytest.raises(AttributeError):
+ data = ruamel.yaml.parse('a: 42') # NOQA
+
+ def test_deprecation_round_trip_load(self) -> None:
+ import ruamel.yaml
+
+ if ruamel.yaml.version_info <= last_to_warn:
+ with pytest.warns(PendingDeprecationWarning):
+ data = ruamel.yaml.round_trip_load('a: 42') # NOQA
+ else:
+ with pytest.raises(AttributeError):
+ data = ruamel.yaml.parse('a: 42') # NOQA
+
+
+class TestYamlTyp:
+ def test_unsafe_deprecation(self) -> None:
+ import ruamel.yaml
+
+ if ruamel.yaml.version_info < (0, 18, 0):
+ yaml = ruamel.yaml.YAML(typ='unsafe')
+ else:
+ with pytest.warns(PendingDeprecationWarning):
+ # with pytest.raises(SystemExit):
+ yaml = ruamel.yaml.YAML(typ='unsafe') # NOQA
+
+ def test_full_load_error(self) -> None:
+ import ruamel.yaml
+
+ yaml = ruamel.yaml.YAML(typ='full', pure=True)
+ with pytest.raises(ruamel.yaml.error.YAMLError):
+ yaml.load('a: b')
+ yaml = ruamel.yaml.YAML(typ='full') # C scanner/loader
+ with pytest.raises(ruamel.yaml.error.YAMLError):
+ yaml.load('a: b')
+
+ def test_full_rt(self) -> None:
+ import os
+ import io
+ import ruamel.yaml
+
+ yaml = ruamel.yaml.YAML(typ='full', pure=True)
+ buf = io.BytesIO()
+ yaml.dump([{'fun': os.system}], buf)
+ print(buf.getvalue())
+ yaml = ruamel.yaml.YAML()
+ data = yaml.load(buf.getvalue())
+ print(data)
+ ts = data[0]['fun']
+ assert 'posix.system' in str(ts.tag)
diff --git a/_test/test_docinfo.py b/_test/test_docinfo.py
new file mode 100644
index 0000000..f05ee26
--- /dev/null
+++ b/_test/test_docinfo.py
@@ -0,0 +1,44 @@
+
+import pytest # type: ignore # NOQA
+from ruamel.yaml.docinfo import Version, version, Tag, DocInfo # NOQA
+
+
+class TestVersion:
+ def test_create_from_integers(self) -> None:
+ v = Version(1, 2)
+ assert v.major == 1
+ assert v.minor == 2
+
+ def test_create_using_generator(self) -> None:
+ v = version(1, 2)
+ assert isinstance(v, Version)
+ assert v.major == 1
+ assert v.minor == 2
+
+ def test_create_from_string_using_generator(self) -> None:
+ v = version('1.2')
+ assert isinstance(v, Version)
+ assert v.major == 1
+ assert v.minor == 2
+
+ def test_create_from_string_extra_param(self) -> None:
+ with pytest.raises(AssertionError):
+ _ = version('1.2', 3)
+
+ def test_create_from_single_integer(self) -> None:
+ with pytest.raises(AssertionError):
+ _ = version(1)
+ with pytest.raises(TypeError):
+ _ = Version(1) # type: ignore
+
+
+class TestDocInfo:
+ def test_empty(self) -> None:
+ di = DocInfo()
+ assert di.requested_version is None
+ assert di.doc_version is None
+ assert di.tags == []
+
+ def test_versions(self) -> None:
+ di = DocInfo(version('1.2'), version('1.1'))
+ assert di.requested_version > di.doc_version # type: ignore
diff --git a/_test/test_documents.py b/_test/test_documents.py
new file mode 100644
index 0000000..b5817f9
--- /dev/null
+++ b/_test/test_documents.py
@@ -0,0 +1,105 @@
+# coding: utf-8
+
+import pytest # type: ignore # NOQA
+
+from roundtrip import round_trip, round_trip_load_all, round_trip_dump_all # type: ignore
+
+
+class TestDocument:
+ def test_single_doc_begin_end(self) -> None:
+ inp = """\
+ ---
+ - a
+ - b
+ ...
+ """
+ round_trip(inp, explicit_start=True, explicit_end=True)
+
+ def test_multi_doc_begin_end(self) -> None:
+ inp = """\
+ ---
+ - a
+ ...
+ ---
+ - b
+ ...
+ """
+ docs = list(round_trip_load_all(inp))
+ assert docs == [['a'], ['b']]
+ out = round_trip_dump_all(docs, explicit_start=True, explicit_end=True)
+ assert out == '---\n- a\n...\n---\n- b\n...\n'
+
+ def test_multi_doc_no_start(self) -> None:
+ inp = """\
+ - a
+ ...
+ ---
+ - b
+ ...
+ """
+ docs = list(round_trip_load_all(inp))
+ assert docs == [['a'], ['b']]
+
+ def test_multi_doc_no_end(self) -> None:
+ inp = """\
+ - a
+ ---
+ - b
+ """
+ docs = list(round_trip_load_all(inp))
+ assert docs == [['a'], ['b']]
+
+ def test_multi_doc_ends_only(self) -> None:
+ # this is ok in 1.2
+ inp = """\
+ - a
+ ...
+ - b
+ ...
+ """
+ docs = list(round_trip_load_all(inp, version=(1, 2)))
+ assert docs == [['a'], ['b']]
+
+ def test_single_scalar_comment(self) -> None:
+ from ruamel import yaml
+
+ inp = """\
+ one # comment
+ two
+ """
+ with pytest.raises(yaml.parser.ParserError):
+ d = list(round_trip_load_all(inp, version=(1, 2))) # NOQA
+
+ def test_scalar_after_seq_document(self) -> None:
+ from ruamel import yaml
+
+ inp = """\
+ [ 42 ]
+ hello
+ """
+ with pytest.raises(yaml.parser.ParserError):
+ d = list(round_trip_load_all(inp, version=(1, 2))) # NOQA
+
+ def test_yunk_after_explicit_document_end(self) -> None:
+ from ruamel import yaml
+
+ inp = """\
+ hello: world
+ ... this is no comment
+ """
+ with pytest.raises(yaml.parser.ParserError):
+ d = list(round_trip_load_all(inp, version=(1, 2))) # NOQA
+
+ def test_multi_doc_ends_only_1_1(self) -> None:
+ from ruamel import yaml
+
+ # this is not ok in 1.1
+ with pytest.raises(yaml.parser.ParserError):
+ inp = """\
+ - a
+ ...
+ - b
+ ...
+ """
+ docs = list(round_trip_load_all(inp, version=(1, 1)))
+ assert docs == [['a'], ['b']] # not True, but not reached
diff --git a/_test/test_fail.py b/_test/test_fail.py
new file mode 100644
index 0000000..516856a
--- /dev/null
+++ b/_test/test_fail.py
@@ -0,0 +1,233 @@
+# coding: utf-8
+
+# there is some work to do
+# provide a failing test xyz and a non-failing xyz_no_fail ( to see
+# what the current failing output is.
+# on fix of ruamel.yaml, move the marked test to the appropriate test (without mark)
+# and remove remove the xyz_no_fail
+
+import pytest # type: ignore
+
+from roundtrip import round_trip, dedent, round_trip_load, round_trip_dump # type: ignore
+
+
+class TestCommentFailures:
+ @pytest.mark.xfail(strict=True) # type: ignore
+ def test_set_comment_before_tag(self) -> None:
+ # no comments before tags
+ round_trip("""
+ # the beginning
+ !!set
+ # or this one?
+ ? a
+ # next one is B (lowercase)
+ ? b # You see? Promised you.
+ ? c
+ # this is the end
+ """)
+
+ def test_set_comment_before_tag_no_fail(self) -> None:
+ # no comments before tags
+ inp = """
+ # the beginning
+ !!set
+ # or this one?
+ ? a
+ # next one is B (lowercase)
+ ? b # You see? Promised you.
+ ? c
+ # this is the end
+ """
+ assert round_trip_dump(round_trip_load(inp)) == dedent("""
+ !!set
+ # or this one?
+ ? a
+ # next one is B (lowercase)
+ ? b # You see? Promised you.
+ ? c
+ # this is the end
+ """)
+
+ @pytest.mark.xfail(strict=True) # type: ignore
+ def test_comment_dash_line(self) -> None:
+ round_trip("""
+ - # abc
+ a: 1
+ b: 2
+ """)
+
+ def test_comment_dash_line_fail(self) -> None:
+ x = """
+ - # abc
+ a: 1
+ b: 2
+ """
+ data = round_trip_load(x)
+ # this is not nice
+ assert round_trip_dump(data) == dedent("""
+ # abc
+ - a: 1
+ b: 2
+ """)
+
+
+class TestIndentFailures:
+ @pytest.mark.xfail(strict=True) # type: ignore
+ def test_indent_not_retained(self) -> None:
+ round_trip("""
+ verbosity: 1 # 0 is minimal output, -1 none
+ base_url: http://gopher.net
+ special_indices: [1, 5, 8]
+ also_special:
+ - a
+ - 19
+ - 32
+ asia and europe: &asia_europe
+ Turkey: Ankara
+ Russia: Moscow
+ countries:
+ Asia:
+ <<: *asia_europe
+ Japan: Tokyo # 東京
+ Europe:
+ <<: *asia_europe
+ Spain: Madrid
+ Italy: Rome
+ Antarctica:
+ - too cold
+ """)
+
+ def test_indent_not_retained_no_fail(self) -> None:
+ inp = """
+ verbosity: 1 # 0 is minimal output, -1 none
+ base_url: http://gopher.net
+ special_indices: [1, 5, 8]
+ also_special:
+ - a
+ - 19
+ - 32
+ asia and europe: &asia_europe
+ Turkey: Ankara
+ Russia: Moscow
+ countries:
+ Asia:
+ <<: *asia_europe
+ Japan: Tokyo # 東京
+ Europe:
+ <<: *asia_europe
+ Spain: Madrid
+ Italy: Rome
+ Antarctica:
+ - too cold
+ """
+ assert round_trip_dump(round_trip_load(inp), indent=4) == dedent("""
+ verbosity: 1 # 0 is minimal output, -1 none
+ base_url: http://gopher.net
+ special_indices: [1, 5, 8]
+ also_special:
+ - a
+ - 19
+ - 32
+ asia and europe: &asia_europe
+ Turkey: Ankara
+ Russia: Moscow
+ countries:
+ Asia:
+ <<: *asia_europe
+ Japan: Tokyo # 東京
+ Europe:
+ <<: *asia_europe
+ Spain: Madrid
+ Italy: Rome
+ Antarctica:
+ - too cold
+ """)
+
+ def Xtest_indent_top_level_no_fail(self) -> None:
+ inp = """
+ - a:
+ - b
+ """
+ round_trip(inp, indent=4)
+
+
+class TestTagFailures:
+ @pytest.mark.xfail(strict=True) # type: ignore
+ def test_standard_short_tag(self) -> None:
+ round_trip("""\
+ !!map
+ name: Anthon
+ location: Germany
+ language: python
+ """)
+
+ def test_standard_short_tag_no_fail(self) -> None:
+ inp = """
+ !!map
+ name: Anthon
+ location: Germany
+ language: python
+ """
+ exp = """
+ name: Anthon
+ location: Germany
+ language: python
+ """
+ assert round_trip_dump(round_trip_load(inp)) == dedent(exp)
+
+
+class TestFlowValues:
+ def test_flow_value_with_colon(self) -> None:
+ inp = """\
+ {a: bcd:efg}
+ """
+ round_trip(inp)
+
+ def test_flow_value_with_colon_quoted(self) -> None:
+ inp = """\
+ {a: 'bcd:efg'}
+ """
+ round_trip(inp, preserve_quotes=True)
+
+
+class TestMappingKey:
+ def test_simple_mapping_key(self) -> None:
+ inp = """\
+ {a: 1, b: 2}: hello world
+ """
+ round_trip(inp, preserve_quotes=True, dump_data=False)
+
+ def test_set_simple_mapping_key(self) -> None:
+ from ruamel.yaml.comments import CommentedKeyMap
+
+ d = {CommentedKeyMap([('a', 1), ('b', 2)]): 'hello world'}
+ exp = dedent("""\
+ {a: 1, b: 2}: hello world
+ """)
+ assert round_trip_dump(d) == exp
+
+ def test_change_key_simple_mapping_key(self) -> None:
+ from ruamel.yaml.comments import CommentedKeyMap
+
+ inp = """\
+ {a: 1, b: 2}: hello world
+ """
+ d = round_trip_load(inp, preserve_quotes=True)
+ d[CommentedKeyMap([('b', 1), ('a', 2)])] = d.pop(CommentedKeyMap([('a', 1), ('b', 2)]))
+ exp = dedent("""\
+ {b: 1, a: 2}: hello world
+ """)
+ assert round_trip_dump(d) == exp
+
+ def test_change_value_simple_mapping_key(self) -> None:
+ from ruamel.yaml.comments import CommentedKeyMap
+
+ inp = """\
+ {a: 1, b: 2}: hello world
+ """
+ d = round_trip_load(inp, preserve_quotes=True)
+ d = {CommentedKeyMap([('a', 1), ('b', 2)]): 'goodbye'}
+ exp = dedent("""\
+ {a: 1, b: 2}: goodbye
+ """)
+ assert round_trip_dump(d) == exp
diff --git a/_test/test_float.py b/_test/test_float.py
new file mode 100644
index 0000000..51745f8
--- /dev/null
+++ b/_test/test_float.py
@@ -0,0 +1,85 @@
+# coding: utf-8
+
+import pytest # type: ignore # NOQA
+
+from roundtrip import round_trip, dedent, round_trip_load, round_trip_dump # type: ignore # NOQA
+
+# http://yaml.org/type/int.html is where underscores in integers are defined
+
+
+class TestFloat:
+ def test_round_trip_non_exp(self) -> None:
+ data = round_trip("""\
+ - 1.0
+ - 1.00
+ - 23.100
+ - -1.0
+ - -1.00
+ - -23.100
+ - 42.
+ - -42.
+ - +42.
+ - .5
+ - +.5
+ - -.5
+ - !!float '42'
+ - !!float '-42'
+ """)
+ print(data)
+ assert 0.999 < data[0] < 1.001
+ assert 0.999 < data[1] < 1.001
+ assert 23.099 < data[2] < 23.101
+ assert 0.999 < -data[3] < 1.001
+ assert 0.999 < -data[4] < 1.001
+ assert 23.099 < -data[5] < 23.101
+ assert 41.999 < data[6] < 42.001
+ assert 41.999 < -data[7] < 42.001
+ assert 41.999 < data[8] < 42.001
+ assert .49 < data[9] < .51
+ assert .49 < data[10] < .51
+ assert -.51 < data[11] < -.49
+ assert 41.99 < data[12] < 42.01
+ assert 41.99 < -data[13] < 42.01
+
+ def test_round_trip_zeros_0(self) -> None:
+ data = round_trip("""\
+ - 0.
+ - +0.
+ - -0.
+ - 0.0
+ - +0.0
+ - -0.0
+ - 0.00
+ - +0.00
+ - -0.00
+ """)
+ print(data)
+ for d in data:
+ assert -0.00001 < d < 0.00001
+
+ def test_round_trip_exp_trailing_dot(self) -> None:
+ data = round_trip("""\
+ - 3.e4
+ """)
+ print(data)
+
+ def test_yaml_1_1_no_dot(self) -> None:
+ from ruamel.yaml.error import MantissaNoDotYAML1_1Warning
+
+ with pytest.warns(MantissaNoDotYAML1_1Warning):
+ round_trip_load("""\
+ %YAML 1.1
+ ---
+ - 1e6
+ """)
+
+
+class TestCalculations:
+ def test_mul_00(self) -> None:
+ # issue 149 reported by jan.brezina@tul.cz
+ d = round_trip_load("""\
+ - 0.1
+ """)
+ d[0] *= -1
+ x = round_trip_dump(d)
+ assert x == '- -0.1\n'
diff --git a/_test/test_flowsequencekey.py b/_test/test_flowsequencekey.py
new file mode 100644
index 0000000..29f157a
--- /dev/null
+++ b/_test/test_flowsequencekey.py
@@ -0,0 +1,25 @@
+# coding: utf-8
+
+"""
+test flow style sequences as keys roundtrip
+
+"""
+
+# import pytest
+
+from roundtrip import round_trip # type: ignore
+
+
+class TestFlowStyleSequenceKey:
+ def test_so_39595807(self) -> None:
+ inp = """\
+ %YAML 1.2
+ ---
+ [2, 3, 4]:
+ a:
+ - 1
+ - 2
+ b: Hello World!
+ c: 'Voilà!'
+ """
+ round_trip(inp, preserve_quotes=True, explicit_start=True, version=(1, 2))
diff --git a/_test/test_indentation.py b/_test/test_indentation.py
new file mode 100644
index 0000000..2b2f005
--- /dev/null
+++ b/_test/test_indentation.py
@@ -0,0 +1,335 @@
+# coding: utf-8
+
+from typing import Any
+import pytest # type: ignore # NOQA
+
+from roundtrip import round_trip, round_trip_load, round_trip_dump, dedent, YAML # type: ignore # NOQA
+
+
+def rt(s: str) -> str:
+ res = round_trip_dump(round_trip_load(s))
+ assert res is not None
+ return res.strip() + '\n' # type: ignore
+
+
+class TestIndent:
+ def test_roundtrip_inline_list(self) -> None:
+ s = 'a: [a, b, c]\n'
+ output = rt(s)
+ assert s == output
+
+ def test_roundtrip_mapping_of_inline_lists(self) -> None:
+ s = dedent("""\
+ a: [a, b, c]
+ j: [k, l, m]
+ """)
+ output = rt(s)
+ assert s == output
+
+ def test_roundtrip_mapping_of_inline_lists_comments(self) -> None:
+ s = dedent("""\
+ # comment A
+ a: [a, b, c]
+ # comment B
+ j: [k, l, m]
+ """)
+ output = rt(s)
+ assert s == output
+
+ def test_roundtrip_mapping_of_inline_sequence_eol_comments(self) -> None:
+ s = dedent("""\
+ # comment A
+ a: [a, b, c] # comment B
+ j: [k, l, m] # comment C
+ """)
+ output = rt(s)
+ assert s == output
+
+ # first test by explicitly setting flow style
+ def test_added_inline_list(self) -> None:
+ s1 = dedent("""
+ a:
+ - b
+ - c
+ - d
+ """)
+ s = 'a: [b, c, d]\n'
+ data = round_trip_load(s1)
+ val = data['a']
+ val.fa.set_flow_style()
+ # print(type(val), '_yaml_format' in dir(val))
+ output = round_trip_dump(data)
+ assert s == output
+
+ # ############ flow mappings
+
+ def test_roundtrip_flow_mapping(self) -> None:
+ s = dedent("""\
+ - {a: 1, b: hallo}
+ - {j: fka, k: 42}
+ """)
+ data = round_trip_load(s)
+ output = round_trip_dump(data)
+ assert s == output
+
+ def test_roundtrip_sequence_of_inline_mappings_eol_comments(self) -> None:
+ s = dedent("""\
+ # comment A
+ - {a: 1, b: hallo} # comment B
+ - {j: fka, k: 42} # comment C
+ """)
+ output = rt(s)
+ assert s == output
+
+ def test_indent_top_level(self) -> None:
+ inp = """
+ - a:
+ - b
+ """
+ round_trip(inp, indent=4)
+
+ def test_set_indent_5_block_list_indent_1(self) -> None:
+ inp = """
+ a:
+ - b: c
+ - 1
+ - d:
+ - 2
+ """
+ round_trip(inp, indent=5, block_seq_indent=1)
+
+ def test_set_indent_4_block_list_indent_2(self) -> None:
+ inp = """
+ a:
+ - b: c
+ - 1
+ - d:
+ - 2
+ """
+ round_trip(inp, indent=4, block_seq_indent=2)
+
+ def test_set_indent_3_block_list_indent_0(self) -> None:
+ inp = """
+ a:
+ - b: c
+ - 1
+ - d:
+ - 2
+ """
+ round_trip(inp, indent=3, block_seq_indent=0)
+
+ def Xtest_set_indent_3_block_list_indent_2(self) -> None:
+ inp = """
+ a:
+ -
+ b: c
+ -
+ 1
+ -
+ d:
+ -
+ 2
+ """
+ round_trip(inp, indent=3, block_seq_indent=2)
+
+ def test_set_indent_3_block_list_indent_2(self) -> None:
+ inp = """
+ a:
+ - b: c
+ - 1
+ - d:
+ - 2
+ """
+ round_trip(inp, indent=3, block_seq_indent=2)
+
+ def Xtest_set_indent_2_block_list_indent_2(self) -> None:
+ inp = """
+ a:
+ -
+ b: c
+ -
+ 1
+ -
+ d:
+ -
+ 2
+ """
+ round_trip(inp, indent=2, block_seq_indent=2)
+
+ # this is how it should be: block_seq_indent stretches the indent
+ def test_set_indent_2_block_list_indent_2(self) -> None:
+ inp = """
+ a:
+ - b: c
+ - 1
+ - d:
+ - 2
+ """
+ round_trip(inp, indent=2, block_seq_indent=2)
+
+ # have to set indent!
+ def test_roundtrip_four_space_indents(self) -> None:
+ # fmt: off
+ s = (
+ 'a:\n'
+ '- foo\n'
+ '- bar\n'
+ )
+ # fmt: on
+ round_trip(s, indent=4)
+
+ def test_roundtrip_four_space_indents_no_fail(self) -> None:
+ inp = """
+ a:
+ - foo
+ - bar
+ """
+ exp = """
+ a:
+ - foo
+ - bar
+ """
+ assert round_trip_dump(round_trip_load(inp)) == dedent(exp)
+
+
+class TestYpkgIndent:
+ def test_00(self) -> None:
+ inp = """
+ name : nano
+ version : 2.3.2
+ release : 1
+ homepage : http://www.nano-editor.org
+ source :
+ - http://www.nano-editor.org/dist/v2.3/nano-2.3.2.tar.gz : ff30924807ea289f5b60106be8
+ license : GPL-2.0
+ summary : GNU nano is an easy-to-use text editor
+ builddeps :
+ - ncurses-devel
+ description: |
+ GNU nano is an easy-to-use text editor originally designed
+ as a replacement for Pico, the ncurses-based editor from the non-free mailer
+ package Pine (itself now available under the Apache License as Alpine).
+ """
+ round_trip(
+ inp, indent=4, block_seq_indent=2, top_level_colon_align=True, prefix_colon=' ',
+ )
+
+
+def guess(s: str) -> Any:
+ from ruamel.yaml.util import load_yaml_guess_indent
+
+ x, y, z = load_yaml_guess_indent(dedent(s))
+ return y, z
+
+
+class TestGuessIndent:
+ def test_guess_20(self) -> None:
+ inp = """\
+ a:
+ - 1
+ """
+ assert guess(inp) == (2, 0)
+
+ def test_guess_42(self) -> None:
+ inp = """\
+ a:
+ - 1
+ """
+ assert guess(inp) == (4, 2)
+
+ def test_guess_42a(self) -> None:
+ # block seq indent prevails over nested key indent level
+ inp = """\
+ b:
+ a:
+ - 1
+ """
+ assert guess(inp) == (4, 2)
+
+ def test_guess_3None(self) -> None:
+ inp = """\
+ b:
+ a: 1
+ """
+ assert guess(inp) == (3, None)
+
+
+class TestSeparateMapSeqIndents:
+ # using uncommon 6 indent with 3 push in as 2 push in automatically
+ # gets you 4 indent even if not set
+ def test_00(self) -> None:
+ # old style
+ yaml = YAML()
+ yaml.indent = 6
+ yaml.block_seq_indent = 3
+ inp = """
+ a:
+ - 1
+ - [1, 2]
+ """
+ yaml.round_trip(inp)
+
+ def test_01(self) -> None:
+ yaml = YAML()
+ yaml.indent(sequence=6)
+ yaml.indent(offset=3)
+ inp = """
+ a:
+ - 1
+ - {b: 3}
+ """
+ yaml.round_trip(inp)
+
+ def test_02(self) -> None:
+ yaml = YAML()
+ yaml.indent(mapping=5, sequence=6, offset=3)
+ inp = """
+ a:
+ b:
+ - 1
+ - [1, 2]
+ """
+ yaml.round_trip(inp)
+
+ def test_03(self) -> None:
+ inp = """
+ a:
+ b:
+ c:
+ - 1
+ - [1, 2]
+ """
+ round_trip(inp, indent=4)
+
+ def test_04(self) -> None:
+ yaml = YAML()
+ yaml.indent(mapping=5, sequence=6)
+ inp = """
+ a:
+ b:
+ - 1
+ - [1, 2]
+ - {d: 3.14}
+ """
+ yaml.round_trip(inp)
+
+ def test_issue_51(self) -> None:
+ yaml = YAML()
+ # yaml.map_indent = 2 # the default
+ yaml.indent(sequence=4, offset=2)
+ yaml.preserve_quotes = True
+ yaml.round_trip("""
+ role::startup::author::rsyslog_inputs:
+ imfile:
+ - ruleset: 'AEM-slinglog'
+ File: '/opt/aem/author/crx-quickstart/logs/error.log'
+ startmsg.regex: '^[-+T.:[:digit:]]*'
+ tag: 'error'
+ - ruleset: 'AEM-slinglog'
+ File: '/opt/aem/author/crx-quickstart/logs/stdout.log'
+ startmsg.regex: '^[-+T.:[:digit:]]*'
+ tag: 'stdout'
+ """)
+
+
+# ############ indentation
diff --git a/_test/test_int.py b/_test/test_int.py
new file mode 100644
index 0000000..d50d53a
--- /dev/null
+++ b/_test/test_int.py
@@ -0,0 +1,32 @@
+# coding: utf-8
+
+import pytest # type: ignore # NOQA
+
+from roundtrip import dedent, round_trip_load, round_trip_dump # type: ignore
+
+# http://yaml.org/type/int.html is where underscores in integers are defined
+
+
+class TestBinHexOct:
+ def test_calculate(self) -> None:
+ # make sure type, leading zero(s) and underscore are preserved
+ s = dedent("""\
+ - 42
+ - 0b101010
+ - 0x_2a
+ - 0x2A
+ - 0o00_52
+ """)
+ d = round_trip_load(s)
+ for idx, elem in enumerate(d):
+ elem -= 21
+ d[idx] = elem
+ for idx, elem in enumerate(d):
+ elem *= 2
+ d[idx] = elem
+ for idx, elem in enumerate(d):
+ t = elem
+ elem **= 2
+ elem //= t
+ d[idx] = elem
+ assert round_trip_dump(d) == s
diff --git a/_test/test_issues.py b/_test/test_issues.py
new file mode 100644
index 0000000..554c7d2
--- /dev/null
+++ b/_test/test_issues.py
@@ -0,0 +1,1205 @@
+# coding: utf-8
+
+from typing import Any
+
+import pytest # type: ignore # NOQA
+
+
+# cannot do "from .roundtrip" because of pytest, so mypy cannot find this
+from roundtrip import ( # type: ignore
+ round_trip,
+ na_round_trip,
+ round_trip_load,
+ round_trip_dump,
+ dedent,
+ save_and_run,
+ YAML,
+) # NOQA
+
+
+class TestIssues:
+ def test_issue_61(self) -> None:
+ s = dedent(
+ """
+ def1: &ANCHOR1
+ key1: value1
+ def: &ANCHOR
+ <<: *ANCHOR1
+ key: value
+ comb:
+ <<: *ANCHOR
+ """,
+ )
+ data = round_trip_load(s)
+ assert str(data['comb']) == str(data['def'])
+ assert str(data['comb']) == "{'key': 'value', 'key1': 'value1'}"
+
+ # def test_issue_82(self, tmpdir):
+ # program_src = r'''
+ # from ruamel import yaml
+ # import re
+ #
+ # class SINumber(yaml.YAMLObject):
+ # PREFIXES = {'k': 1e3, 'M': 1e6, 'G': 1e9}
+ # yaml_loader = yaml.Loader
+ # yaml_dumper = yaml.Dumper
+ # yaml_tag = '!si'
+ # yaml_implicit_pattern = re.compile(
+ # r'^(?P<value>[0-9]+(?:\.[0-9]+)?)(?P<prefix>[kMG])$')
+ #
+ # @classmethod
+ # def from_yaml(cls, loader, node):
+ # return cls(node.value)
+ #
+ # @classmethod
+ # def to_yaml(cls, dumper, data):
+ # return dumper.represent_scalar(cls.yaml_tag, str(data))
+ #
+ # def __init__(self, *args):
+ # m = self.yaml_implicit_pattern.match(args[0])
+ # self.value = float(m.groupdict()['value'])
+ # self.prefix = m.groupdict()['prefix']
+ #
+ # def __str__(self) -> None:
+ # return str(self.value)+self.prefix
+ #
+ # def __int__(self) -> None:
+ # return int(self.value*self.PREFIXES[self.prefix])
+ #
+ # # This fails:
+ # yaml.add_implicit_resolver(SINumber.yaml_tag, SINumber.yaml_implicit_pattern)
+ #
+ # ret = yaml.load("""
+ # [1,2,3, !si 10k, 100G]
+ # """, Loader=yaml.Loader)
+ # for idx, l in enumerate([1, 2, 3, 10000, 100000000000]):
+ # assert int(ret[idx]) == l
+ # '''
+ # assert save_and_run(dedent(program_src), tmpdir) == 0
+
+ def test_issue_82rt(self, tmpdir: Any) -> None:
+ yaml_str = '[1, 2, 3, !si 10k, 100G]\n'
+ x = round_trip(yaml_str, preserve_quotes=True) # NOQA
+
+ def test_issue_102(self) -> None:
+ yaml_str = dedent(
+ """
+ var1: #empty
+ var2: something #notempty
+ var3: {} #empty object
+ var4: {a: 1} #filled object
+ var5: [] #empty array
+ """,
+ )
+ x = round_trip(yaml_str, preserve_quotes=True) # NOQA
+
+ def test_issue_150(self) -> None:
+ from ruamel.yaml import YAML
+
+ inp = """\
+ base: &base_key
+ first: 123
+ second: 234
+
+ child:
+ <<: *base_key
+ third: 345
+ """
+ yaml = YAML()
+ data = yaml.load(inp)
+ child = data['child']
+ assert 'second' in dict(**child)
+
+ def test_issue_160(self) -> None:
+ from ruamel.yaml.compat import StringIO
+
+ s = dedent(
+ """\
+ root:
+ # a comment
+ - {some_key: "value"}
+
+ foo: 32
+ bar: 32
+ """,
+ )
+ a = round_trip_load(s)
+ del a['root'][0]['some_key']
+ buf = StringIO()
+ round_trip_dump(a, buf, block_seq_indent=4)
+ exp = dedent(
+ """\
+ root:
+ # a comment
+ - {}
+
+ foo: 32
+ bar: 32
+ """,
+ )
+ assert buf.getvalue() == exp
+
+ def test_issue_161(self) -> None:
+ yaml_str = dedent(
+ """\
+ mapping-A:
+ key-A:{}
+ mapping-B:
+ """,
+ )
+ for comment in ['', ' # no-newline', ' # some comment\n', '\n']:
+ s = yaml_str.format(comment)
+ res = round_trip(s) # NOQA
+
+ def test_issue_161a(self) -> None:
+ yaml_str = dedent(
+ """\
+ mapping-A:
+ key-A:{}
+ mapping-B:
+ """,
+ )
+ for comment in ['\n# between']:
+ s = yaml_str.format(comment)
+ res = round_trip(s) # NOQA
+
+ def test_issue_163(self) -> None:
+ s = dedent(
+ """\
+ some-list:
+ # List comment
+ - {}
+ """,
+ )
+ x = round_trip(s, preserve_quotes=True) # NOQA
+
+ json_str = (
+ r'{"sshKeys":[{"name":"AETROS\/google-k80-1","uses":0,"getLastUse":0,'
+ '"fingerprint":"MD5:19:dd:41:93:a1:a3:f5:91:4a:8e:9b:d0:ae:ce:66:4c",'
+ '"created":1509497961}]}'
+ )
+
+ json_str2 = '{"abc":[{"a":"1", "uses":0}]}'
+
+ def test_issue_172(self) -> None:
+ x = round_trip_load(TestIssues.json_str2) # NOQA
+ x = round_trip_load(TestIssues.json_str) # NOQA
+
+ def test_issue_176(self) -> None:
+ # basic request by Stuart Berg
+ from ruamel.yaml import YAML
+
+ yaml = YAML()
+ seq = yaml.load('[1,2,3]')
+ seq[:] = [1, 2, 3, 4]
+
+ def test_issue_176_preserve_comments_on_extended_slice_assignment(self) -> None:
+ yaml_str = dedent(
+ """\
+ - a
+ - b # comment
+ - c # commment c
+ # comment c+
+ - d
+
+ - e # comment
+ """,
+ )
+ seq = round_trip_load(yaml_str)
+ seq[1::2] = ['B', 'D']
+ res = round_trip_dump(seq)
+ assert res == yaml_str.replace(' b ', ' B ').replace(' d\n', ' D\n')
+
+ def test_issue_176_test_slicing(self) -> None:
+ mss = round_trip_load('[0, 1, 2, 3, 4]')
+ assert len(mss) == 5
+ assert mss[2:2] == []
+ assert mss[2:4] == [2, 3]
+ assert mss[1::2] == [1, 3]
+
+ # slice assignment
+ m = mss[:]
+ m[2:2] = [42]
+ assert m == [0, 1, 42, 2, 3, 4]
+
+ m = mss[:]
+ m[:3] = [42, 43, 44]
+ assert m == [42, 43, 44, 3, 4]
+ m = mss[:]
+ m[2:] = [42, 43, 44]
+ assert m == [0, 1, 42, 43, 44]
+ m = mss[:]
+ m[:] = [42, 43, 44]
+ assert m == [42, 43, 44]
+
+ # extend slice assignment
+ m = mss[:]
+ m[2:4] = [42, 43, 44]
+ assert m == [0, 1, 42, 43, 44, 4]
+ m = mss[:]
+ m[1::2] = [42, 43]
+ assert m == [0, 42, 2, 43, 4]
+ m = mss[:]
+ with pytest.raises(TypeError, match='too many'):
+ m[1::2] = [42, 43, 44]
+ with pytest.raises(TypeError, match='not enough'):
+ m[1::2] = [42]
+ m = mss[:]
+ m += [5]
+ m[1::2] = [42, 43, 44]
+ assert m == [0, 42, 2, 43, 4, 44]
+
+ # deleting
+ m = mss[:]
+ del m[1:3]
+ assert m == [0, 3, 4]
+ m = mss[:]
+ del m[::2]
+ assert m == [1, 3]
+ m = mss[:]
+ del m[:]
+ assert m == []
+
+ def test_issue_184(self) -> None:
+ yaml_str = dedent(
+ """\
+ test::test:
+ # test
+ foo:
+ bar: baz
+ """,
+ )
+ d = round_trip_load(yaml_str)
+ d['bar'] = 'foo'
+ d.yaml_add_eol_comment('test1', 'bar')
+ assert round_trip_dump(d) == yaml_str + 'bar: foo # test1\n'
+
+ def test_issue_219(self) -> None:
+ yaml_str = dedent(
+ """\
+ [StackName: AWS::StackName]
+ """,
+ )
+ d = round_trip_load(yaml_str) # NOQA
+
+ def test_issue_219a(self) -> None:
+ yaml_str = dedent(
+ """\
+ [StackName:
+ AWS::StackName]
+ """,
+ )
+ d = round_trip_load(yaml_str) # NOQA
+
+ def test_issue_220(self, tmpdir: Any) -> None:
+ program_src = r'''
+ from ruamel.yaml import YAML
+
+ yaml_str = """\
+ ---
+ foo: ["bar"]
+ """
+
+ yaml = YAML(typ='safe', pure=True)
+ d = yaml.load(yaml_str)
+ print(d)
+ '''
+ assert save_and_run(dedent(program_src), tmpdir, optimized=True) == 0
+
+ def test_issue_221_add(self) -> None:
+ from ruamel.yaml.comments import CommentedSeq
+
+ a = CommentedSeq([1, 2, 3])
+ a + [4, 5]
+
+ def test_issue_221_sort(self) -> None:
+ from ruamel.yaml import YAML
+ from ruamel.yaml.compat import StringIO
+
+ yaml = YAML()
+ inp = dedent(
+ """\
+ - d
+ - a # 1
+ - c # 3
+ - e # 5
+ - b # 2
+ """,
+ )
+ a = yaml.load(dedent(inp))
+ a.sort()
+ buf = StringIO()
+ yaml.dump(a, buf)
+ exp = dedent(
+ """\
+ - a # 1
+ - b # 2
+ - c # 3
+ - d
+ - e # 5
+ """,
+ )
+ assert buf.getvalue() == exp
+
+ def test_issue_221_sort_reverse(self) -> None:
+ from ruamel.yaml import YAML
+ from ruamel.yaml.compat import StringIO
+
+ yaml = YAML()
+ inp = dedent(
+ """\
+ - d
+ - a # 1
+ - c # 3
+ - e # 5
+ - b # 2
+ """,
+ )
+ a = yaml.load(dedent(inp))
+ a.sort(reverse=True)
+ buf = StringIO()
+ yaml.dump(a, buf)
+ exp = dedent(
+ """\
+ - e # 5
+ - d
+ - c # 3
+ - b # 2
+ - a # 1
+ """,
+ )
+ assert buf.getvalue() == exp
+
+ def test_issue_221_sort_key(self) -> None:
+ from ruamel.yaml import YAML
+ from ruamel.yaml.compat import StringIO
+
+ yaml = YAML()
+ inp = dedent(
+ """\
+ - four
+ - One # 1
+ - Three # 3
+ - five # 5
+ - two # 2
+ """,
+ )
+ a = yaml.load(dedent(inp))
+ a.sort(key=str.lower)
+ buf = StringIO()
+ yaml.dump(a, buf)
+ exp = dedent(
+ """\
+ - five # 5
+ - four
+ - One # 1
+ - Three # 3
+ - two # 2
+ """,
+ )
+ assert buf.getvalue() == exp
+
+ def test_issue_221_sort_key_reverse(self) -> None:
+ from ruamel.yaml import YAML
+ from ruamel.yaml.compat import StringIO
+
+ yaml = YAML()
+ inp = dedent(
+ """\
+ - four
+ - One # 1
+ - Three # 3
+ - five # 5
+ - two # 2
+ """,
+ )
+ a = yaml.load(dedent(inp))
+ a.sort(key=str.lower, reverse=True)
+ buf = StringIO()
+ yaml.dump(a, buf)
+ exp = dedent(
+ """\
+ - two # 2
+ - Three # 3
+ - One # 1
+ - four
+ - five # 5
+ """,
+ )
+ assert buf.getvalue() == exp
+
+ def test_issue_222(self) -> None:
+ import ruamel.yaml
+ from ruamel.yaml.compat import StringIO
+
+ yaml = ruamel.yaml.YAML(typ='safe')
+ buf = StringIO()
+ yaml.dump(['012923'], buf)
+ assert buf.getvalue() == "['012923']\n"
+
+ def test_issue_223(self) -> None:
+ import ruamel.yaml
+
+ yaml = ruamel.yaml.YAML(typ='safe')
+ yaml.load('phone: 0123456789')
+
+ def test_issue_232(self) -> None:
+ import ruamel.yaml
+
+ yaml = YAML(typ='safe', pure=True)
+
+ with pytest.raises(ruamel.yaml.parser.ParserError):
+ yaml.load(']')
+ with pytest.raises(ruamel.yaml.parser.ParserError):
+ yaml.load('{]')
+
+ def test_issue_233(self) -> None:
+ from ruamel.yaml import YAML
+ import json
+
+ yaml = YAML()
+ data = yaml.load('{}')
+ json_str = json.dumps(data) # NOQA
+
+ def test_issue_233a(self) -> None:
+ from ruamel.yaml import YAML
+ import json
+
+ yaml = YAML()
+ data = yaml.load('[]')
+ json_str = json.dumps(data) # NOQA
+
+ def test_issue_234(self) -> None:
+ from ruamel.yaml import YAML
+
+ inp = dedent(
+ """\
+ - key: key1
+ ctx: [one, two]
+ help: one
+ cmd: >
+ foo bar
+ foo bar
+ """,
+ )
+ yaml = YAML(typ='safe', pure=True)
+ data = yaml.load(inp)
+ fold = data[0]['cmd']
+ print(repr(fold))
+ assert '\a' not in fold
+
+ def test_issue_236(self) -> None:
+ inp = """
+ conf:
+ xx: {a: "b", c: []}
+ asd: "nn"
+ """
+ d = round_trip(inp, preserve_quotes=True) # NOQA
+
+ def test_issue_238(self, tmpdir: Any) -> None:
+ program_src = r"""
+ import ruamel.yaml
+ from ruamel.yaml.compat import StringIO
+
+ yaml = ruamel.yaml.YAML(typ='unsafe')
+
+
+ class A:
+ def __setstate__(self, d):
+ self.__dict__ = d
+
+
+ class B:
+ pass
+
+
+ a = A()
+ b = B()
+
+ a.x = b
+ b.y = [b]
+ assert a.x.y[0] == a.x
+
+ buf = StringIO()
+ yaml.dump(a, buf)
+
+ data = yaml.load(buf.getvalue())
+ assert data.x.y[0] == data.x
+ """
+ assert save_and_run(dedent(program_src), tmpdir) == 0
+
+ def test_issue_239(self) -> None:
+ inp = """
+ first_name: Art
+ occupation: Architect
+ # I'm safe
+ about: Art Vandelay is a fictional character that George invents...
+ # we are not :(
+ # help me!
+ ---
+ # what?!
+ hello: world
+ # someone call the Batman
+ foo: bar # or quz
+ # Lost again
+ ---
+ I: knew
+ # final words
+ """
+ d = YAML().round_trip_all(inp) # NOQA
+
+ def test_issue_242(self) -> None:
+ from ruamel.yaml.comments import CommentedMap
+
+ d0 = CommentedMap([('a', 'b')])
+ assert d0['a'] == 'b'
+
+ def test_issue_245(self) -> None:
+ from ruamel.yaml import YAML
+
+ inp = """
+ d: yes
+ """
+ for typ in ['safepure', 'rt', 'safe']:
+ if typ.endswith('pure'):
+ pure = True
+ typ = typ[:-4]
+ else:
+ pure = None
+
+ yaml = YAML(typ=typ, pure=pure)
+ yaml.version = (1, 1)
+ d = yaml.load(inp)
+ print(typ, yaml.parser, yaml.resolver)
+ assert d['d'] is True
+
+ def test_issue_249(self) -> None:
+ yaml = YAML()
+ inp = dedent(
+ """\
+ # comment
+ -
+ - 1
+ - 2
+ - 3
+ """,
+ )
+ exp = dedent(
+ """\
+ # comment
+ - - 1
+ - 2
+ - 3
+ """,
+ )
+ yaml.round_trip(inp, outp=exp) # NOQA
+
+ def test_issue_250(self) -> None:
+ inp = """
+ # 1.
+ - - 1
+ # 2.
+ - map: 2
+ # 3.
+ - 4
+ """
+ d = round_trip(inp) # NOQA
+
+ # @pytest.mark.xfail(strict=True, reason='bla bla', raises=AssertionError)
+ def test_issue_279(self) -> None:
+ from ruamel.yaml import YAML
+ from ruamel.yaml.compat import StringIO
+
+ yaml = YAML()
+ yaml.indent(sequence=4, offset=2)
+ inp = dedent(
+ """\
+ experiments:
+ - datasets:
+ # ATLAS EWK
+ - {dataset: ATLASWZRAP36PB, frac: 1.0}
+ - {dataset: ATLASZHIGHMASS49FB, frac: 1.0}
+ """,
+ )
+ a = yaml.load(inp)
+ buf = StringIO()
+ yaml.dump(a, buf)
+ print(buf.getvalue())
+ assert buf.getvalue() == inp
+
+ def test_issue_280(self) -> None:
+ from ruamel.yaml import YAML
+ from ruamel.yaml.representer import RepresenterError
+ from collections import namedtuple
+ from sys import stdout
+
+ T = namedtuple('T', ('a', 'b'))
+ t = T(1, 2)
+ yaml = YAML()
+ with pytest.raises(RepresenterError, match='cannot represent'):
+ yaml.dump({'t': t}, stdout)
+
+ def test_issue_282(self) -> None:
+ # update from list of tuples caused AttributeError
+ import ruamel.yaml
+
+ yaml_data = ruamel.yaml.comments.CommentedMap([('a', 'apple'), ('b', 'banana')])
+ yaml_data.update([('c', 'cantaloupe')])
+ yaml_data.update({'d': 'date', 'k': 'kiwi'})
+ assert 'c' in yaml_data.keys()
+ assert 'c' in yaml_data._ok
+
+ def test_issue_284(self) -> None:
+ import ruamel.yaml
+
+ inp = dedent(
+ """\
+ plain key: in-line value
+ : # Both empty
+ "quoted key":
+ - entry
+ """,
+ )
+ yaml = ruamel.yaml.YAML(typ='rt')
+ yaml.version = (1, 2)
+ d = yaml.load(inp)
+ assert d[None] is None
+
+ yaml = ruamel.yaml.YAML(typ='rt')
+ yaml.version = (1, 1)
+ with pytest.raises(ruamel.yaml.parser.ParserError, match='expected <block end>'):
+ d = yaml.load(inp)
+
+ def test_issue_285(self) -> None:
+ from ruamel.yaml import YAML
+
+ yaml = YAML()
+ inp = dedent(
+ """\
+ %YAML 1.1
+ ---
+ - y
+ - n
+ - Y
+ - N
+ """,
+ )
+ a = yaml.load(inp)
+ assert a[0]
+ assert a[2]
+ assert not a[1]
+ assert not a[3]
+
+ def test_issue_286(self) -> None:
+ from ruamel.yaml import YAML
+ from ruamel.yaml.compat import StringIO
+
+ yaml = YAML()
+ inp = dedent(
+ """\
+ parent_key:
+ - sub_key: sub_value
+
+ # xxx""",
+ )
+ a = yaml.load(inp)
+ a['new_key'] = 'new_value'
+ buf = StringIO()
+ yaml.dump(a, buf)
+ assert buf.getvalue().endswith('xxx\nnew_key: new_value\n')
+
+ def test_issue_288(self) -> None:
+ import sys
+ from ruamel.yaml.compat import StringIO
+ from ruamel.yaml import YAML
+
+ yamldoc = dedent(
+ """\
+ ---
+ # Reusable values
+ aliases:
+ # First-element comment
+ - &firstEntry First entry
+ # Second-element comment
+ - &secondEntry Second entry
+
+ # Third-element comment is
+ # a multi-line value
+ - &thirdEntry Third entry
+
+ # EOF Comment
+ """,
+ )
+
+ yaml = YAML()
+ yaml.indent(mapping=2, sequence=4, offset=2)
+ yaml.explicit_start = True
+ yaml.preserve_quotes = True
+ yaml.width = sys.maxsize
+ data = yaml.load(yamldoc)
+ buf = StringIO()
+ yaml.dump(data, buf)
+ assert buf.getvalue() == yamldoc
+
+ def test_issue_288a(self) -> None:
+ import sys
+ from ruamel.yaml.compat import StringIO
+ from ruamel.yaml import YAML
+
+ yamldoc = dedent(
+ """\
+ ---
+ # Reusable values
+ aliases:
+ # First-element comment
+ - &firstEntry First entry
+ # Second-element comment
+ - &secondEntry Second entry
+
+ # Third-element comment is
+ # a multi-line value
+ - &thirdEntry Third entry
+
+ # EOF Comment
+ """,
+ )
+
+ yaml = YAML()
+ yaml.indent(mapping=2, sequence=4, offset=2)
+ yaml.explicit_start = True
+ yaml.preserve_quotes = True
+ yaml.width = sys.maxsize
+ data = yaml.load(yamldoc)
+ buf = StringIO()
+ yaml.dump(data, buf)
+ assert buf.getvalue() == yamldoc
+
+ def test_issue_290(self) -> None:
+ import sys
+ from ruamel.yaml.compat import StringIO
+ from ruamel.yaml import YAML
+
+ yamldoc = dedent(
+ """\
+ ---
+ aliases:
+ # Folded-element comment
+ # for a multi-line value
+ - &FoldedEntry >
+ THIS IS A
+ FOLDED, MULTI-LINE
+ VALUE
+
+ # Literal-element comment
+ # for a multi-line value
+ - &literalEntry |
+ THIS IS A
+ LITERAL, MULTI-LINE
+ VALUE
+
+ # Plain-element comment
+ - &plainEntry Plain entry
+ """,
+ )
+
+ yaml = YAML()
+ yaml.indent(mapping=2, sequence=4, offset=2)
+ yaml.explicit_start = True
+ yaml.preserve_quotes = True
+ yaml.width = sys.maxsize
+ data = yaml.load(yamldoc)
+ buf = StringIO()
+ yaml.dump(data, buf)
+ assert buf.getvalue() == yamldoc
+
+ def test_issue_290a(self) -> None:
+ import sys
+ from ruamel.yaml.compat import StringIO
+ from ruamel.yaml import YAML
+
+ yamldoc = dedent(
+ """\
+ ---
+ aliases:
+ # Folded-element comment
+ # for a multi-line value
+ - &FoldedEntry >
+ THIS IS A
+ FOLDED, MULTI-LINE
+ VALUE
+
+ # Literal-element comment
+ # for a multi-line value
+ - &literalEntry |
+ THIS IS A
+ LITERAL, MULTI-LINE
+ VALUE
+
+ # Plain-element comment
+ - &plainEntry Plain entry
+ """,
+ )
+
+ yaml = YAML()
+ yaml.indent(mapping=2, sequence=4, offset=2)
+ yaml.explicit_start = True
+ yaml.preserve_quotes = True
+ yaml.width = sys.maxsize
+ data = yaml.load(yamldoc)
+ buf = StringIO()
+ yaml.dump(data, buf)
+ assert buf.getvalue() == yamldoc
+
+ # @pytest.mark.xfail(strict=True, reason='should fail pre 0.15.100', raises=AssertionError)
+ def test_issue_295(self) -> None:
+ # deepcopy also makes a copy of the start and end mark, and these did not
+ # have any comparison beyond their ID, which of course changed, breaking
+ # some old merge_comment code
+ import copy
+
+ inp = dedent(
+ """
+ A:
+ b:
+ # comment
+ - l1
+ - l2
+
+ C:
+ d: e
+ f:
+ # comment2
+ - - l31
+ - l32
+ - l33: '5'
+ """,
+ )
+ data = round_trip_load(inp) # NOQA
+ dc = copy.deepcopy(data)
+ assert round_trip_dump(dc) == inp
+
+ def test_issue_300(self) -> None:
+ from ruamel.yaml import YAML
+
+ inp = dedent(
+ """
+ %YAML 1.2
+ %TAG ! tag:example.com,2019/path#fragment
+ ---
+ null
+ """,
+ )
+ YAML().load(inp)
+
+ def test_issue_300a(self) -> None:
+ import ruamel.yaml
+
+ inp = dedent(
+ """
+ %YAML 1.1
+ %TAG ! tag:example.com,2019/path#fragment
+ ---
+ null
+ """,
+ )
+ yaml = YAML()
+ with pytest.raises(
+ ruamel.yaml.scanner.ScannerError, match='while scanning a directive',
+ ):
+ yaml.load(inp)
+
+ def test_issue_304(self) -> None:
+ inp = """
+ %YAML 1.2
+ %TAG ! tag:example.com,2019:
+ ---
+ !foo null
+ ...
+ """
+ d = na_round_trip(inp) # NOQA
+
+ def test_issue_305(self) -> None:
+ inp = """
+ %YAML 1.2
+ ---
+ !<tag:example.com,2019/path#foo> null
+ ...
+ """
+ d = na_round_trip(inp) # NOQA
+
+ def test_issue_307(self) -> None:
+ inp = """
+ %YAML 1.2
+ %TAG ! tag:example.com,2019/path#
+ ---
+ null
+ ...
+ """
+ d = na_round_trip(inp) # NOQA
+
+ def test_issue_445(self) -> None:
+ from ruamel.yaml import YAML
+ from ruamel.yaml.compat import StringIO
+
+ yaml = YAML()
+ yaml.version = '1.1' # type: ignore
+ data = yaml.load('quote: I have seen things')
+ buf = StringIO()
+ yaml.dump(data, buf)
+ assert buf.getvalue() == '%YAML 1.1\n---\nquote: I have seen things\n'
+ yaml = YAML()
+ yaml.version = [1, 1] # type: ignore
+ data = yaml.load('quote: I have seen things')
+ buf = StringIO()
+ yaml.dump(data, buf)
+ assert buf.getvalue() == '%YAML 1.1\n---\nquote: I have seen things\n'
+
+ def test_issue_447(self) -> None:
+ from ruamel.yaml import YAML
+
+ YAML().load('{\n\t"FOO": "BAR"\n}')
+
+ def test_issue_449(self) -> None:
+ inp = """\
+ emoji_index: !!python/name:materialx.emoji.twemoji
+ """
+ d = na_round_trip(inp) # NOQA
+
+ def test_issue_455(self) -> None:
+ from ruamel.yaml import YAML
+
+ cm = YAML().map(a=97, b=98)
+ cm.update({'c': 42, 'd': 196})
+ cm.update(c=99, d=100)
+ prev = None
+ for k, v in cm.items():
+ if prev is not None:
+ assert prev + 1 == v
+ prev = v
+ assert ord(k) == v
+ assert len(cm) == 4
+
+ def test_issue_453(self) -> None:
+ from io import StringIO
+ from ruamel.yaml import YAML
+
+ inp = dedent(
+ """
+ to-merge: &anchor
+ merge-key: should not be duplicated
+
+ to-merge2: &anchor2
+ merge-key2: should not be duplicated
+
+ usage:
+ <<: [*anchor, *anchor2]
+ usage-key: usage-value
+ """,
+ )
+ yaml = YAML()
+ data = yaml.load(inp)
+ data['usage'].insert(0, 'insert-key', 'insert-value')
+ out_stream = StringIO()
+ yaml.dump(data, out_stream)
+ result = out_stream.getvalue()
+ print(result)
+ assert inp.replace('usage:\n', 'usage:\n insert-key: insert-value\n') == result
+
+ def test_issue_454(self) -> None:
+ inp = """
+ test1: 🎉
+ test2: "🎉"
+ test3: '🎉'
+ """
+ d = round_trip(inp, preserve_quotes=True) # NOQA
+
+ def test_so_75631454(self) -> None:
+ from ruamel.yaml import YAML
+ from ruamel.yaml.compat import StringIO
+
+ inp = dedent(
+ """
+ test:
+ long: "This is a sample text
+ across two lines."
+ """,
+ )
+ yaml = YAML()
+ yaml.preserve_quotes = True
+ yaml.indent(mapping=4)
+ yaml.width = 27
+ data = yaml.load(inp)
+ buf = StringIO()
+ yaml.dump(data, buf)
+ assert buf.getvalue() == inp
+
+ def test_issue_458(self) -> None:
+ from io import StringIO
+ from ruamel.yaml import YAML
+
+ yaml = YAML()
+ out_stream = StringIO()
+ in_string = 'a' * 128
+ yaml.dump(in_string, out_stream)
+ result = out_stream.getvalue()
+ assert in_string == result.splitlines()[0]
+
+ def test_issue_459(self) -> None:
+ from io import StringIO
+ from ruamel.yaml import YAML
+
+ MYOBJ = {
+ 'data': dedent(
+ """\
+ example: "first"
+ data:
+ - flag: true
+ integer: 1
+ float: 1.0
+ string: "this is a string"
+ list:
+ - first
+ - second
+ - third
+ circle:
+ x: 10cm
+ y: 10cm
+ radius: 2.24cm
+
+ - flag: false
+ integer: 2
+ float: 2.0
+ string: "this is another string"
+ list:
+ - first
+ - second
+ - third
+ circle:
+ x: 20cm
+ y: 20cm
+ radius: 2.24cm
+ """,
+ ),
+ }
+ yaml = YAML()
+ yaml.width = 60
+ out_stream = StringIO()
+ yaml.dump([MYOBJ], out_stream)
+ data = yaml.load(out_stream.getvalue())
+ assert data[0]['data'] == MYOBJ['data']
+
+ def test_issue_461(self) -> None:
+ from ruamel.yaml import YAML
+
+ yaml = YAML()
+
+ inp = dedent(
+ """
+ first name: Roy
+ last name: Rogers
+ city: somewhere
+ """,
+ )
+ yaml = YAML()
+ data = yaml.load(inp)
+ data.pop('last name')
+ assert data.pop('not there', 'xxx') == 'xxx'
+ data.insert(1, 'last name', 'Beaty', comment='he has seen things')
+
+ def test_issue_463(self) -> None:
+ import sys
+ from ruamel.yaml.compat import StringIO
+ from ruamel.yaml import YAML
+
+ yaml = YAML()
+
+ inp = dedent(
+ """
+ first_name: Art
+ """,
+ )
+ data = yaml.load(inp)
+ _ = data.merge
+ data.insert(0, 'some_key', 'test')
+ yaml.dump(data, sys.stdout)
+ buf = StringIO()
+ yaml.dump(data, buf)
+ exp = dedent(
+ """
+ some_key: test
+ first_name: Art
+ """,
+ )
+ assert buf.getvalue() == exp
+
+ def test_issue_464(self) -> None:
+ # document end marker without newline threw error in 0.17.27
+ from ruamel.yaml import YAML
+
+ yaml = YAML()
+ yaml.load('---\na: True\n...')
+
+ def test_issue_467(self) -> None:
+ # cannot change the default constructor, following test will fail
+ import ruamel.yaml
+
+ yaml = ruamel.yaml.YAML()
+ old_constructor = yaml.constructor.add_constructor(
+ yaml.resolver.DEFAULT_MAPPING_TAG,
+ lambda x, y: None,
+ )
+ # this should be solved by the copy to the Constructor instance
+ if old_constructor is not None:
+ yaml.constructor.add_constructor(
+ yaml.resolver.DEFAULT_MAPPING_TAG,
+ old_constructor,
+ )
+
+ yaml = ruamel.yaml.YAML()
+ # for k, v in yaml.constructor.yaml_constructors.items():
+ # print(k, v)
+ assert yaml.load('a: b') is not None
+
+ def test_issue_480(self) -> None:
+ import sys
+ import ruamel.yaml
+
+ yaml = ruamel.yaml.YAML()
+ data = yaml.load(
+ dedent("""
+ # hi
+ {}
+ """),
+ )
+ yaml.dump(data, sys.stdout)
+
+ def test_issue_482(self) -> None:
+ import ruamel.yaml
+ from collections import OrderedDict
+
+ def _ordered_constructor(loader: Any, node: Any) -> Any:
+ loader.flatten_mapping(node)
+ return OrderedDict(loader.construct_pairs(node))
+
+ content = 'foo: bar'
+
+ yaml = ruamel.yaml.YAML(typ='safe', pure=True)
+ old_constructor = yaml.constructor.add_constructor(
+ yaml.Resolver.DEFAULT_MAPPING_TAG,
+ _ordered_constructor,
+ )
+
+ data = yaml.load(content)
+ print('data', data, type(data))
+ assert isinstance(data, OrderedDict)
+ if old_constructor is not None:
+ yaml.constructor.add_constructor(
+ yaml.resolver.DEFAULT_MAPPING_TAG,
+ old_constructor,
+ )
+
+# @pytest.mark.xfail(strict=True, reason='bla bla', raises=AssertionError)
+# def test_issue_ xxx(self) -> None:
+# inp = """
+# """
+# d = round_trip(inp) # NOQA
diff --git a/_test/test_json_numbers.py b/_test/test_json_numbers.py
new file mode 100644
index 0000000..08f39d0
--- /dev/null
+++ b/_test/test_json_numbers.py
@@ -0,0 +1,58 @@
+# coding: utf-8
+
+import pytest # type: ignore # NOQA
+
+import json
+
+from typing import Any
+
+
+def load(s: str, typ: Any = float) -> float:
+ import ruamel.yaml
+
+ yaml = ruamel.yaml.YAML()
+ x = '{"low": %s }' % (s)
+ print('input: [%s]' % (s), repr(x))
+ # just to check it is loadable json
+ res = json.loads(x)
+ assert isinstance(res['low'], typ)
+ ret_val = yaml.load(x)
+ print(ret_val)
+ return ret_val['low'] # type: ignore
+
+
+class TestJSONNumbers:
+ # based on http://stackoverflow.com/a/30462009/1307905
+ # yaml number regex: http://yaml.org/spec/1.2/spec.html#id2804092
+ #
+ # -? [1-9] ( \. [0-9]* [1-9] )? ( e [-+] [1-9] [0-9]* )?
+ #
+ # which is not a superset of the JSON numbers
+ def test_json_number_float(self) -> None:
+ for x in (
+ y.split('#')[0].strip()
+ for y in """
+ 1.0 # should fail on YAML spec on 1-9 allowed as single digit
+ -1.0
+ 1e-06
+ 3.1e-5
+ 3.1e+5
+ 3.1e5 # should fail on YAML spec: no +- after e
+ """.splitlines()
+ ):
+ if not x:
+ continue
+ res = load(x)
+ assert isinstance(res, float)
+
+ def test_json_number_int(self) -> None:
+ for x in (
+ y.split('#')[0].strip()
+ for y in """
+ 42
+ """.splitlines()
+ ):
+ if not x:
+ continue
+ res = load(x, int)
+ assert isinstance(res, int)
diff --git a/_test/test_line_col.py b/_test/test_line_col.py
new file mode 100644
index 0000000..6b172b0
--- /dev/null
+++ b/_test/test_line_col.py
@@ -0,0 +1,92 @@
+# coding: utf-8
+
+import pytest # type: ignore # NOQA
+
+from roundtrip import round_trip, dedent, round_trip_load, round_trip_dump # type: ignore # NOQA
+
+from typing import Any
+
+
+def load(s: str) -> Any:
+ return round_trip_load(dedent(s))
+
+
+class TestLineCol:
+ def test_item_00(self) -> None:
+ data = load("""
+ - a
+ - e
+ - [b, d]
+ - c
+ """)
+ assert data[2].lc.line == 2
+ assert data[2].lc.col == 2
+
+ def test_item_01(self) -> None:
+ data = load("""
+ - a
+ - e
+ - {x: 3}
+ - c
+ """)
+ assert data[2].lc.line == 2
+ assert data[2].lc.col == 2
+
+ def test_item_02(self) -> None:
+ data = load("""
+ - a
+ - e
+ - !!set {x, y}
+ - c
+ """)
+ assert data[2].lc.line == 2
+ assert data[2].lc.col == 2
+
+ def test_item_03(self) -> None:
+ data = load("""
+ - a
+ - e
+ - !!omap
+ - x: 1
+ - y: 3
+ - c
+ """)
+ assert data[2].lc.line == 2
+ assert data[2].lc.col == 2
+
+ def test_item_04(self) -> None:
+ data = load("""
+ # testing line and column based on SO
+ # http://stackoverflow.com/questions/13319067/
+ - key1: item 1
+ key2: item 2
+ - key3: another item 1
+ key4: another item 2
+ """)
+ assert data[0].lc.line == 2
+ assert data[0].lc.col == 2
+ assert data[1].lc.line == 4
+ assert data[1].lc.col == 2
+
+ def test_pos_mapping(self) -> None:
+ data = load("""
+ a: 1
+ b: 2
+ c: 3
+ # comment
+ klm: 42
+ d: 4
+ """)
+ assert data.lc.key('klm') == (4, 0)
+ assert data.lc.value('klm') == (4, 5)
+
+ def test_pos_sequence(self) -> None:
+ data = load("""
+ - a
+ - b
+ - c
+ # next one!
+ - klm
+ - d
+ """)
+ assert data.lc.item(3) == (4, 2)
diff --git a/_test/test_literal.py b/_test/test_literal.py
new file mode 100644
index 0000000..fcc949c
--- /dev/null
+++ b/_test/test_literal.py
@@ -0,0 +1,361 @@
+# coding: utf-8
+
+import pytest # type: ignore # NOQA
+
+from roundtrip import YAML # type: ignore # does an automatic dedent on load
+
+
+"""
+YAML 1.0 allowed root level literal style without indentation:
+ "Usually top level nodes are not indented" (example 4.21 in 4.6.3)
+YAML 1.1 is a bit vague but says:
+ "Regardless of style, scalar content must always be indented by at least one space"
+ (4.4.3)
+ "In general, the document’s node is indented as if it has a parent indented at -1 spaces."
+ (4.3.3)
+YAML 1.2 is again clear about root literal level scalar after directive in example 9.5:
+
+%YAML 1.2
+--- |
+%!PS-Adobe-2.0
+...
+%YAML1.2
+---
+# Empty
+...
+"""
+
+
+class TestNoIndent:
+ def test_root_literal_scalar_indent_example_9_5(self) -> None:
+ yaml = YAML()
+ s = '%!PS-Adobe-2.0'
+ inp = """
+ --- |
+ {}
+ """
+ d = yaml.load(inp.format(s))
+ print(d)
+ assert d == s + '\n'
+
+ def test_root_literal_scalar_no_indent(self) -> None:
+ yaml = YAML()
+ s = 'testing123'
+ inp = """
+ --- |
+ {}
+ """
+ d = yaml.load(inp.format(s))
+ print(d)
+ assert d == s + '\n'
+
+ def test_root_literal_scalar_no_indent_1_1(self) -> None:
+ yaml = YAML()
+ s = 'testing123'
+ inp = """
+ %YAML 1.1
+ --- |
+ {}
+ """
+ d = yaml.load(inp.format(s))
+ print(d)
+ assert d == s + '\n'
+
+ def test_root_literal_scalar_no_indent_1_1_old_style(self) -> None:
+ from textwrap import dedent
+ from ruamel.yaml import YAML
+
+ yaml = YAML(typ='safe', pure=True)
+ s = 'testing123'
+ inp = """
+ %YAML 1.1
+ --- |
+ {}
+ """
+ d = yaml.load(dedent(inp.format(s)))
+ print(d)
+ assert d == s + '\n'
+
+ def test_root_literal_scalar_no_indent_1_1_no_raise(self) -> None:
+ # from ruamel.yaml.parser import ParserError
+
+ yaml = YAML()
+ yaml.root_level_block_style_scalar_no_indent_error_1_1 = True
+ s = 'testing123'
+ # with pytest.raises(ParserError):
+ if True:
+ inp = """
+ %YAML 1.1
+ --- |
+ {}
+ """
+ yaml.load(inp.format(s))
+
+ def test_root_literal_scalar_indent_offset_one(self) -> None:
+ yaml = YAML()
+ s = 'testing123'
+ inp = """
+ --- |1
+ {}
+ """
+ d = yaml.load(inp.format(s))
+ print(d)
+ assert d == s + '\n'
+
+ def test_root_literal_scalar_indent_offset_four(self) -> None:
+ yaml = YAML()
+ s = 'testing123'
+ inp = """
+ --- |4
+ {}
+ """
+ d = yaml.load(inp.format(s))
+ print(d)
+ assert d == s + '\n'
+
+ def test_root_literal_scalar_indent_offset_two_leading_space(self) -> None:
+ yaml = YAML()
+ s = ' testing123'
+ inp = """
+ --- |4
+ {s}
+ {s}
+ """
+ d = yaml.load(inp.format(s=s))
+ print(d)
+ assert d == (s + '\n') * 2
+
+ def test_root_literal_scalar_no_indent_special(self) -> None:
+ yaml = YAML()
+ s = '%!PS-Adobe-2.0'
+ inp = """
+ --- |
+ {}
+ """
+ d = yaml.load(inp.format(s))
+ print(d)
+ assert d == s + '\n'
+
+ def test_root_folding_scalar_indent(self) -> None:
+ yaml = YAML()
+ s = '%!PS-Adobe-2.0'
+ inp = """
+ --- >
+ {}
+ """
+ d = yaml.load(inp.format(s))
+ print(d)
+ assert d == s + '\n'
+
+ def test_root_folding_scalar_no_indent(self) -> None:
+ yaml = YAML()
+ s = 'testing123'
+ inp = """
+ --- >
+ {}
+ """
+ d = yaml.load(inp.format(s))
+ print(d)
+ assert d == s + '\n'
+
+ def test_root_folding_scalar_no_indent_special(self) -> None:
+ yaml = YAML()
+ s = '%!PS-Adobe-2.0'
+ inp = """
+ --- >
+ {}
+ """
+ d = yaml.load(inp.format(s))
+ print(d)
+ assert d == s + '\n'
+
+ def test_root_literal_multi_doc(self) -> None:
+ yaml = YAML(typ='safe', pure=True)
+ s1 = 'abc'
+ s2 = 'klm'
+ inp = """
+ --- |-
+ {}
+ --- |
+ {}
+ """
+ for idx, d1 in enumerate(yaml.load_all(inp.format(s1, s2))):
+ print('d1:', d1)
+ assert ['abc', 'klm\n'][idx] == d1
+
+ def test_root_literal_doc_indent_directives_end(self) -> None:
+ yaml = YAML()
+ yaml.explicit_start = True
+ inp = """
+ --- |-
+ %YAML 1.3
+ ---
+ this: is a test
+ """
+ yaml.round_trip(inp)
+
+ def test_root_literal_doc_indent_document_end(self) -> None:
+ yaml = YAML()
+ yaml.explicit_start = True
+ inp = """
+ --- |-
+ some more
+ ...
+ text
+ """
+ yaml.round_trip(inp)
+
+ def test_root_literal_doc_indent_marker(self) -> None:
+ yaml = YAML()
+ yaml.explicit_start = True
+ inp = """
+ --- |2
+ some more
+ text
+ """
+ d = yaml.load(inp)
+ print(type(d), repr(d))
+ yaml.round_trip(inp)
+
+ def test_nested_literal_doc_indent_marker(self) -> None:
+ yaml = YAML()
+ yaml.explicit_start = True
+ inp = """
+ ---
+ a: |2
+ some more
+ text
+ """
+ d = yaml.load(inp)
+ print(type(d), repr(d))
+ yaml.round_trip(inp)
+
+
+class Test_RoundTripLiteral:
+ def test_rt_root_literal_scalar_no_indent(self) -> None:
+ yaml = YAML()
+ yaml.explicit_start = True
+ s = 'testing123'
+ ys = """
+ --- |
+ {}
+ """
+ ys = ys.format(s)
+ d = yaml.load(ys)
+ yaml.dump(d, compare=ys)
+
+ def test_rt_root_literal_scalar_indent(self) -> None:
+ yaml = YAML()
+ yaml.explicit_start = True
+ yaml.indent = 4
+ s = 'testing123'
+ ys = """
+ --- |
+ {}
+ """
+ ys = ys.format(s)
+ d = yaml.load(ys)
+ yaml.dump(d, compare=ys)
+
+ def test_rt_root_plain_scalar_no_indent(self) -> None:
+ yaml = YAML()
+ yaml.explicit_start = True
+ yaml.indent = 0
+ s = 'testing123'
+ ys = """
+ ---
+ {}
+ """
+ ys = ys.format(s)
+ d = yaml.load(ys)
+ yaml.dump(d, compare=ys)
+
+ def test_rt_root_plain_scalar_expl_indent(self) -> None:
+ yaml = YAML()
+ yaml.explicit_start = True
+ yaml.indent = 4
+ s = 'testing123'
+ ys = """
+ ---
+ {}
+ """
+ ys = ys.format(s)
+ d = yaml.load(ys)
+ yaml.dump(d, compare=ys)
+
+ def test_rt_root_sq_scalar_expl_indent(self) -> None:
+ yaml = YAML()
+ yaml.explicit_start = True
+ yaml.indent = 4
+ s = "'testing: 123'"
+ ys = """
+ ---
+ {}
+ """
+ ys = ys.format(s)
+ d = yaml.load(ys)
+ yaml.dump(d, compare=ys)
+
+ def test_rt_root_dq_scalar_expl_indent(self) -> None:
+ # if yaml.indent is the default (None)
+ # then write after the directive indicator
+ yaml = YAML()
+ yaml.explicit_start = True
+ yaml.indent = 0
+ s = '"\'testing123"'
+ ys = """
+ ---
+ {}
+ """
+ ys = ys.format(s)
+ d = yaml.load(ys)
+ yaml.dump(d, compare=ys)
+
+ def test_rt_root_literal_scalar_no_indent_no_eol(self) -> None:
+ yaml = YAML()
+ yaml.explicit_start = True
+ s = 'testing123'
+ ys = """
+ --- |-
+ {}
+ """
+ ys = ys.format(s)
+ d = yaml.load(ys)
+ yaml.dump(d, compare=ys)
+
+ def test_rt_non_root_literal_scalar(self) -> None:
+ yaml = YAML()
+ s = 'testing123'
+ ys = """
+ - |
+ {}
+ """
+ ys = ys.format(s)
+ d = yaml.load(ys)
+ yaml.dump(d, compare=ys)
+
+ def test_regular_spaces(self) -> None:
+ import ruamel.yaml
+
+ yaml = ruamel.yaml.YAML()
+ ys = "key: |\n\n\n content\n"
+ d = yaml.load(ys)
+ assert d['key'] == '\n\ncontent\n'
+
+ def test_irregular_spaces_content(self) -> None:
+ import ruamel.yaml
+
+ yaml = ruamel.yaml.YAML()
+ ys = "key: |\n \n \n irregular content\n"
+ with pytest.raises(ruamel.yaml.scanner.ScannerError):
+ d = yaml.load(ys)
+ print(d)
+
+ def test_irregular_spaces_comment(self) -> None:
+ import ruamel.yaml
+
+ yaml = ruamel.yaml.YAML()
+ ys = "key: |\n \n \n # comment\n"
+ with pytest.raises(ruamel.yaml.scanner.ScannerError):
+ d = yaml.load(ys)
+ print(d)
diff --git a/_test/test_none.py b/_test/test_none.py
new file mode 100644
index 0000000..dfb6c4c
--- /dev/null
+++ b/_test/test_none.py
@@ -0,0 +1,41 @@
+# coding: utf-8
+
+import pytest # type: ignore # NOQA
+from roundtrip import round_trip_load, round_trip_dump # type: ignore
+
+
+class TestNone:
+ def test_dump00(self) -> None:
+ data = None
+ s = round_trip_dump(data)
+ assert s == 'null\n...\n'
+ d = round_trip_load(s)
+ assert d == data
+
+ def test_dump01(self) -> None:
+ data = None
+ s = round_trip_dump(data, explicit_end=True)
+ assert s == 'null\n...\n'
+ d = round_trip_load(s)
+ assert d == data
+
+ def test_dump02(self) -> None:
+ data = None
+ s = round_trip_dump(data, explicit_end=False)
+ assert s == 'null\n...\n'
+ d = round_trip_load(s)
+ assert d == data
+
+ def test_dump03(self) -> None:
+ data = None
+ s = round_trip_dump(data, explicit_start=True)
+ assert s == '---\n...\n'
+ d = round_trip_load(s)
+ assert d == data
+
+ def test_dump04(self) -> None:
+ data = None
+ s = round_trip_dump(data, explicit_start=True, explicit_end=False)
+ assert s == '---\n...\n'
+ d = round_trip_load(s)
+ assert d == data
diff --git a/_test/test_numpy.py b/_test/test_numpy.py
new file mode 100644
index 0000000..24eb768
--- /dev/null
+++ b/_test/test_numpy.py
@@ -0,0 +1,24 @@
+# coding: utf-8
+
+# try:
+# import numpy
+# except: # NOQA
+# numpy = None
+
+
+# def Xtest_numpy() -> None:
+# import ruamel.yaml
+#
+# if numpy is None:
+# return
+# data = numpy.arange(10)
+# print('data', type(data), data)
+#
+# buf = io.BytesIO()
+# ruamel.yaml.dump(data) # needs updating to use buffer
+# yaml_str = buf.getvalue().decode('utf-8')
+# datb = ruamel.yaml.load(yaml_str)
+# print('datb', type(datb), datb)
+#
+# print('\nYAML', yaml_str)
+# assert data == datb
diff --git a/_test/test_program_config.py b/_test/test_program_config.py
new file mode 100644
index 0000000..cda40d3
--- /dev/null
+++ b/_test/test_program_config.py
@@ -0,0 +1,59 @@
+# coding: utf-8
+
+import pytest # type: ignore # NOQA
+
+# import ruamel.yaml
+from roundtrip import round_trip # type: ignore
+
+
+class TestProgramConfig:
+ def test_application_arguments(self) -> None:
+ # application configur
+ round_trip("""
+ args:
+ username: anthon
+ passwd: secret
+ fullname: Anthon van der Neut
+ tmux:
+ session-name: test
+ loop:
+ wait: 10
+ """)
+
+ def test_single(self) -> None:
+ # application configuration
+ round_trip("""
+ # default arguments for the program
+ args: # needed to prevent comment wrapping
+ # this should be your username
+ username: anthon
+ passwd: secret # this is plaintext don't reuse \
+# important/system passwords
+ fullname: Anthon van der Neut
+ tmux:
+ session-name: test # make sure this doesn't clash with
+ # other sessions
+ loop: # looping related defaults
+ # experiment with the following
+ wait: 10
+ # no more argument info to pass
+ """)
+
+ def test_multi(self) -> None:
+ # application configuration
+ round_trip("""
+ # default arguments for the program
+ args: # needed to prevent comment wrapping
+ # this should be your username
+ username: anthon
+ passwd: secret # this is plaintext don't reuse
+ # important/system passwords
+ fullname: Anthon van der Neut
+ tmux:
+ session-name: test # make sure this doesn't clash with
+ # other sessions
+ loop: # looping related defaults
+ # experiment with the following
+ wait: 10
+ # no more argument info to pass
+ """)
diff --git a/_test/test_spec_examples.py b/_test/test_spec_examples.py
new file mode 100644
index 0000000..9e14a15
--- /dev/null
+++ b/_test/test_spec_examples.py
@@ -0,0 +1,293 @@
+# coding: utf-8
+
+from roundtrip import YAML # type: ignore
+import pytest # type: ignore # NOQA
+
+
+def test_example_2_1() -> None:
+ yaml = YAML()
+ yaml.round_trip("""
+ - Mark McGwire
+ - Sammy Sosa
+ - Ken Griffey
+ """)
+
+
+@pytest.mark.xfail(strict=True) # type: ignore
+def test_example_2_2() -> None:
+ yaml = YAML()
+ yaml.mapping_value_align = True
+ yaml.round_trip("""
+ hr: 65 # Home runs
+ avg: 0.278 # Batting average
+ rbi: 147 # Runs Batted In
+ """)
+
+
+def test_example_2_3() -> None:
+ yaml = YAML()
+ yaml.indent(sequence=4, offset=2)
+ yaml.round_trip("""
+ american:
+ - Boston Red Sox
+ - Detroit Tigers
+ - New York Yankees
+ national:
+ - New York Mets
+ - Chicago Cubs
+ - Atlanta Braves
+ """)
+
+
+@pytest.mark.xfail(strict=True) # type: ignore
+def test_example_2_4() -> None:
+ yaml = YAML()
+ yaml.mapping_value_align = True
+ yaml.round_trip("""
+ -
+ name: Mark McGwire
+ hr: 65
+ avg: 0.278
+ -
+ name: Sammy Sosa
+ hr: 63
+ avg: 0.288
+ """)
+
+
+@pytest.mark.xfail(strict=True) # type: ignore
+def test_example_2_5() -> None:
+ yaml = YAML()
+ yaml.flow_sequence_element_align = True
+ yaml.round_trip("""
+ - [name , hr, avg ]
+ - [Mark McGwire, 65, 0.278]
+ - [Sammy Sosa , 63, 0.288]
+ """)
+
+
+@pytest.mark.xfail(strict=True) # type: ignore
+def test_example_2_6() -> None:
+ yaml = YAML()
+ # yaml.flow_mapping_final_comma = False
+ yaml.flow_mapping_one_element_per_line = True
+ yaml.round_trip("""
+ Mark McGwire: {hr: 65, avg: 0.278}
+ Sammy Sosa: {
+ hr: 63,
+ avg: 0.288
+ }
+ """)
+
+
+@pytest.mark.xfail(strict=True) # type: ignore
+def test_example_2_7() -> None:
+ yaml = YAML()
+ yaml.round_trip_all("""
+ # Ranking of 1998 home runs
+ ---
+ - Mark McGwire
+ - Sammy Sosa
+ - Ken Griffey
+
+ # Team ranking
+ ---
+ - Chicago Cubs
+ - St Louis Cardinals
+ """)
+
+
+def test_example_2_8() -> None:
+ yaml = YAML()
+ yaml.explicit_start = True
+ yaml.explicit_end = True
+ yaml.round_trip_all("""
+ ---
+ time: 20:03:20
+ player: Sammy Sosa
+ action: strike (miss)
+ ...
+ ---
+ time: 20:03:47
+ player: Sammy Sosa
+ action: grand slam
+ ...
+ """)
+
+
+def test_example_2_9() -> None:
+ yaml = YAML()
+ yaml.explicit_start = True
+ yaml.indent(sequence=4, offset=2)
+ yaml.round_trip("""
+ ---
+ hr: # 1998 hr ranking
+ - Mark McGwire
+ - Sammy Sosa
+ rbi:
+ # 1998 rbi ranking
+ - Sammy Sosa
+ - Ken Griffey
+ """)
+
+
+@pytest.mark.xfail(strict=True) # type: ignore
+def test_example_2_10() -> None:
+ yaml = YAML()
+ yaml.explicit_start = True
+ yaml.indent(sequence=4, offset=2)
+ yaml.round_trip("""
+ ---
+ hr:
+ - Mark McGwire
+ # Following node labeled SS
+ - &SS Sammy Sosa
+ rbi:
+ - *SS # Subsequent occurrence
+ - Ken Griffey
+ """)
+
+
+@pytest.mark.xfail(strict=True) # type: ignore
+def test_example_2_11() -> None:
+ yaml = YAML()
+ yaml.round_trip("""
+ ? - Detroit Tigers
+ - Chicago cubs
+ :
+ - 2001-07-23
+
+ ? [ New York Yankees,
+ Atlanta Braves ]
+ : [ 2001-07-02, 2001-08-12,
+ 2001-08-14 ]
+ """)
+
+
+@pytest.mark.xfail(strict=True) # type: ignore
+def test_example_2_12() -> None:
+ yaml = YAML()
+ yaml.explicit_start = True
+ yaml.round_trip("""
+ ---
+ # Products purchased
+ - item : Super Hoop
+ quantity: 1
+ - item : Basketball
+ quantity: 4
+ - item : Big Shoes
+ quantity: 1
+ """)
+
+
+@pytest.mark.xfail(strict=True) # type: ignore
+def test_example_2_13() -> None:
+ yaml = YAML()
+ yaml.round_trip(r"""
+ # ASCII Art
+ --- |
+ \//||\/||
+ // || ||__
+ """)
+
+
+@pytest.mark.xfail(strict=True) # type: ignore
+def test_example_2_14() -> None:
+ yaml = YAML()
+ yaml.explicit_start = True
+ yaml.indent(root_scalar=2) # needs to be added
+ yaml.round_trip("""
+ --- >
+ Mark McGwire's
+ year was crippled
+ by a knee injury.
+ """)
+
+
+@pytest.mark.xfail(strict=True) # type: ignore
+def test_example_2_15() -> None:
+ yaml = YAML()
+ yaml.round_trip("""
+ >
+ Sammy Sosa completed another
+ fine season with great stats.
+
+ 63 Home Runs
+ 0.288 Batting Average
+
+ What a year!
+ """)
+
+
+def test_example_2_16() -> None:
+ yaml = YAML()
+ yaml.round_trip("""
+ name: Mark McGwire
+ accomplishment: >
+ Mark set a major league
+ home run record in 1998.
+ stats: |
+ 65 Home Runs
+ 0.278 Batting Average
+ """)
+
+
+@pytest.mark.xfail( # type: ignore
+ strict=True, reason='cannot YAML dump escape sequences (\n) as hex and normal',
+)
+def test_example_2_17() -> None:
+ yaml = YAML()
+ yaml.allow_unicode = False
+ yaml.preserve_quotes = True
+ yaml.round_trip(r"""
+ unicode: "Sosa did fine.\u263A"
+ control: "\b1998\t1999\t2000\n"
+ hex esc: "\x0d\x0a is \r\n"
+
+ single: '"Howdy!" he cried.'
+ quoted: ' # Not a ''comment''.'
+ tie-fighter: '|\-*-/|'
+ """)
+
+
+@pytest.mark.xfail(strict=True, # type: ignore # NOQA
+ reason='non-literal/folding multiline scalars not supported')
+def test_example_2_18() -> None:
+ yaml = YAML()
+ yaml.round_trip("""
+ plain:
+ This unquoted scalar
+ spans many lines.
+
+ quoted: "So does this
+ quoted scalar.\n"
+ """)
+
+
+@pytest.mark.xfail(strict=True, reason='leading + on decimal dropped') # type: ignore
+def test_example_2_19() -> None:
+ yaml = YAML()
+ yaml.round_trip("""
+ canonical: 12345
+ decimal: +12345
+ octal: 0o14
+ hexadecimal: 0xC
+ """)
+
+
+@pytest.mark.xfail(strict=True, reason='case of NaN not preserved') # type: ignore
+def test_example_2_20() -> None:
+ yaml = YAML()
+ yaml.round_trip("""
+ canonical: 1.23015e+3
+ exponential: 12.3015e+02
+ fixed: 1230.15
+ negative infinity: -.inf
+ not a number: .NaN
+ """)
+
+
+def Xtest_example_2_X() -> None:
+ yaml = YAML()
+ yaml.round_trip("""
+ """)
diff --git a/_test/test_string.py b/_test/test_string.py
new file mode 100644
index 0000000..cf826f7
--- /dev/null
+++ b/_test/test_string.py
@@ -0,0 +1,213 @@
+# coding: utf-8
+
+"""
+various test cases for string scalars in YAML files
+'|' for preserved newlines
+'>' for folded (newlines become spaces)
+
+and the chomping modifiers:
+'-' for stripping: final line break and any trailing empty lines are excluded
+'+' for keeping: final line break and empty lines are preserved
+'' for clipping: final line break preserved, empty lines at end not
+ included in content (no modifier)
+
+"""
+
+import pytest # type: ignore
+import platform
+
+# from ruamel.yaml.compat import ordereddict
+from roundtrip import round_trip, dedent, round_trip_load, round_trip_dump # type: ignore # NOQA
+
+
+class TestLiteralScalarString:
+ def test_basic_string(self) -> None:
+ round_trip("""
+ a: abcdefg
+ """)
+
+ def test_quoted_integer_string(self) -> None:
+ round_trip("""
+ a: '12345'
+ """)
+
+ @pytest.mark.skipif( # type: ignore
+ platform.python_implementation() == 'Jython', reason='Jython throws RepresenterError',
+ )
+ def test_preserve_string(self) -> None:
+ inp = """
+ a: |
+ abc
+ def
+ """
+ round_trip(inp, intermediate=dict(a='abc\ndef\n'))
+
+ @pytest.mark.skipif( # type: ignore
+ platform.python_implementation() == 'Jython', reason='Jython throws RepresenterError',
+ )
+ def test_preserve_string_strip(self) -> None:
+ s = """
+ a: |-
+ abc
+ def
+
+ """
+ round_trip(s, intermediate=dict(a='abc\ndef'))
+
+ @pytest.mark.skipif( # type: ignore
+ platform.python_implementation() == 'Jython', reason='Jython throws RepresenterError',
+ )
+ def test_preserve_string_keep(self) -> None:
+ # with pytest.raises(AssertionError) as excinfo:
+ inp = """
+ a: |+
+ ghi
+ jkl
+
+
+ b: x
+ """
+ round_trip(inp, intermediate=dict(a='ghi\njkl\n\n\n', b='x'))
+
+ @pytest.mark.skipif( # type: ignore
+ platform.python_implementation() == 'Jython', reason='Jython throws RepresenterError',
+ )
+ def test_preserve_string_keep_at_end(self) -> None:
+ # at EOF you have to specify the ... to get proper "closure"
+ # of the multiline scalar
+ inp = """
+ a: |+
+ ghi
+ jkl
+
+ ...
+ """
+ round_trip(inp, intermediate=dict(a='ghi\njkl\n\n'))
+
+ def test_fold_string(self) -> None:
+ inp = """
+ a: >
+ abc
+ def
+
+ """
+ round_trip(inp)
+
+ def test_fold_string_strip(self) -> None:
+ inp = """
+ a: >-
+ abc
+ def
+
+ """
+ round_trip(inp)
+
+ def test_fold_string_keep(self) -> None:
+ with pytest.raises(AssertionError) as excinfo: # NOQA
+ inp = """
+ a: >+
+ abc
+ def
+
+ """
+ round_trip(inp, intermediate=dict(a='abc def\n\n'))
+
+
+class TestQuotedScalarString:
+ def test_single_quoted_string(self) -> None:
+ inp = """
+ a: 'abc'
+ """
+ round_trip(inp, preserve_quotes=True)
+
+ def test_double_quoted_string(self) -> None:
+ inp = """
+ a: "abc"
+ """
+ round_trip(inp, preserve_quotes=True)
+
+ def test_non_preserved_double_quoted_string(self) -> None:
+ inp = """
+ a: "abc"
+ """
+ exp = """
+ a: abc
+ """
+ round_trip(inp, outp=exp)
+
+
+class TestReplace:
+ """inspired by issue 110 from sandres23"""
+
+ def test_replace_preserved_scalar_string(self) -> None:
+ import ruamel
+
+ s = dedent("""\
+ foo: |
+ foo
+ foo
+ bar
+ foo
+ """)
+ data = round_trip_load(s, preserve_quotes=True)
+ so = data['foo'].replace('foo', 'bar', 2)
+ assert isinstance(so, ruamel.yaml.scalarstring.LiteralScalarString)
+ assert so == dedent("""
+ bar
+ bar
+ bar
+ foo
+ """)
+
+ def test_replace_double_quoted_scalar_string(self) -> None:
+ import ruamel
+
+ s = dedent("""\
+ foo: "foo foo bar foo"
+ """)
+ data = round_trip_load(s, preserve_quotes=True)
+ so = data['foo'].replace('foo', 'bar', 2)
+ assert isinstance(so, ruamel.yaml.scalarstring.DoubleQuotedScalarString)
+ assert so == 'bar bar bar foo'
+
+
+class TestWalkTree:
+ def test_basic(self) -> None:
+ from ruamel.yaml.comments import CommentedMap
+ from ruamel.yaml.scalarstring import walk_tree
+
+ data = CommentedMap()
+ data[1] = 'a'
+ data[2] = 'with\nnewline\n'
+ walk_tree(data)
+ exp = """\
+ 1: a
+ 2: |
+ with
+ newline
+ """
+ assert round_trip_dump(data) == dedent(exp)
+
+ def test_map(self) -> None:
+ from ruamel.yaml.compat import ordereddict
+ from ruamel.yaml.comments import CommentedMap
+ from ruamel.yaml.scalarstring import walk_tree, preserve_literal
+ from ruamel.yaml.scalarstring import DoubleQuotedScalarString as dq
+ from ruamel.yaml.scalarstring import SingleQuotedScalarString as sq
+
+ data = CommentedMap()
+ data[1] = 'a'
+ data[2] = 'with\nnew : line\n'
+ data[3] = '${abc}'
+ data[4] = 'almost:mapping'
+ m = ordereddict([('\n', preserve_literal), ('${', sq), (':', dq)])
+ walk_tree(data, map=m)
+ exp = """\
+ 1: a
+ 2: |
+ with
+ new : line
+ 3: '${abc}'
+ 4: "almost:mapping"
+ """
+ assert round_trip_dump(data) == dedent(exp)
diff --git a/_test/test_tag.py b/_test/test_tag.py
new file mode 100644
index 0000000..4cd96ac
--- /dev/null
+++ b/_test/test_tag.py
@@ -0,0 +1,219 @@
+# coding: utf-8
+
+import pytest # type: ignore # NOQA
+from typing import Any
+
+from roundtrip import round_trip, round_trip_load, YAML # type: ignore
+
+
+def register_xxx(**kw: Any) -> None:
+ from ruamel import yaml
+
+ class XXX(yaml.comments.CommentedMap):
+ @staticmethod
+ def yaml_dump(dumper: Any, data: Any) -> Any:
+ return dumper.represent_mapping('!xxx', data)
+
+ @classmethod
+ def yaml_load(cls, constructor: Any, node: Any) -> Any:
+ data = cls()
+ yield data
+ constructor.construct_mapping(node, data)
+
+ yaml.add_constructor('!xxx', XXX.yaml_load, constructor=yaml.RoundTripConstructor)
+ yaml.add_representer(XXX, XXX.yaml_dump, representer=yaml.RoundTripRepresenter)
+
+
+class TestIndentFailures:
+ def test_tag(self) -> None:
+ round_trip(
+ """\
+ !!python/object:__main__.Developer
+ name: Anthon
+ location: Germany
+ language: python
+ """,
+ )
+
+ def test_full_tag(self) -> None:
+ round_trip(
+ """\
+ !!tag:yaml.org,2002:python/object:__main__.Developer
+ name: Anthon
+ location: Germany
+ language: python
+ """,
+ )
+
+ def test_standard_tag(self) -> None:
+ round_trip(
+ """\
+ !!tag:yaml.org,2002:python/object:map
+ name: Anthon
+ location: Germany
+ language: python
+ """,
+ )
+
+ def test_Y1(self) -> None:
+ round_trip(
+ """\
+ !yyy
+ name: Anthon
+ location: Germany
+ language: python
+ """,
+ )
+
+ def test_Y2(self) -> None:
+ round_trip(
+ """\
+ !!yyy
+ name: Anthon
+ location: Germany
+ language: python
+ """,
+ )
+
+ # @pytest.mark.xfail(strict=True) # type: ignore
+ def test_spec_6_26_tag_shorthands(self) -> None:
+ from ruamel.yaml import YAML
+ from io import StringIO
+ from textwrap import dedent
+
+ inp = dedent(
+ """\
+ %TAG !e! tag:example.com,2000:app/
+ ---
+ - !local foo
+ - !!str bar
+ - !e!tag%21 baz
+ """,
+ )
+ yaml = YAML()
+ data = yaml.load(inp)
+ buf = StringIO()
+ yaml.dump(data, buf)
+ print('buf:\n', buf.getvalue(), sep='')
+ assert buf.getvalue() == inp
+
+
+class TestTagGeneral:
+ def test_unknow_handle(self) -> None:
+ from ruamel.yaml.parser import ParserError
+
+ with pytest.raises(ParserError):
+ round_trip(
+ """\
+ %TAG !x! tag:example.com,2000:app/
+ ---
+ - !y!tag%21 baz
+ """,
+ )
+
+
+class TestRoundTripCustom:
+ def test_X1(self) -> None:
+ register_xxx()
+ round_trip(
+ """\
+ !xxx
+ name: Anthon
+ location: Germany
+ language: python
+ """,
+ )
+
+ @pytest.mark.xfail(strict=True) # type: ignore
+ def test_X_pre_tag_comment(self) -> None:
+ register_xxx()
+ round_trip(
+ """\
+ -
+ # hello
+ !xxx
+ name: Anthon
+ location: Germany
+ language: python
+ """,
+ )
+
+ @pytest.mark.xfail(strict=True) # type: ignore
+ def test_X_post_tag_comment(self) -> None:
+ register_xxx()
+ round_trip(
+ """\
+ - !xxx
+ # hello
+ name: Anthon
+ location: Germany
+ language: python
+ """,
+ )
+
+ def test_scalar_00(self) -> None:
+ # https://stackoverflow.com/a/45967047/1307905
+ round_trip(
+ """\
+ Outputs:
+ Vpc:
+ Value: !Ref: vpc # first tag
+ Export:
+ Name: !Sub "${AWS::StackName}-Vpc" # second tag
+ """,
+ )
+
+
+class TestIssue201:
+ def test_encoded_unicode_tag(self) -> None:
+ round_trip_load(
+ """
+ s: !!python/%75nicode 'abc'
+ """,
+ )
+
+
+class TestImplicitTaggedNodes:
+ def test_scalar(self) -> None:
+ data = round_trip(
+ """\
+ - !SString abcdefg
+ - !SFloat 1.0
+ - !SInt 1961
+ - !SBool true
+ - !SLit |
+ glitter in the dark near the Tanhäuser gate
+ """,
+ )
+ # tagged scalers have string or string types as value
+ assert data[0].count('d') == 1
+ assert data[1].count('1') == 1
+ assert data[2].count('1') == 2
+ assert data[3].count('u') == 1
+ assert data[4].count('a') == 4
+
+ def test_mapping(self) -> None:
+ round_trip(
+ """\
+ - !Mapping {a: 1, b: 2}
+ """,
+ )
+
+ def test_sequence(self) -> None:
+ yaml = YAML()
+ yaml.brace_single_entry_mapping_in_flow_sequence = True
+ yaml.mapping_value_align = True
+ yaml.round_trip(
+ """
+ - !Sequence [a, {b: 1}, {c: {d: 3}}]
+ """,
+ )
+
+ def test_sequence2(self) -> None:
+ yaml = YAML()
+ yaml.mapping_value_align = True
+ yaml.round_trip(
+ """
+ - !Sequence [a, b: 1, c: {d: 3}]
+ """,
+ )
diff --git a/_test/test_version.py b/_test/test_version.py
new file mode 100644
index 0000000..e508f32
--- /dev/null
+++ b/_test/test_version.py
@@ -0,0 +1,180 @@
+# coding: utf-8
+
+import pytest # type: ignore # NOQA
+from typing import Any, Optional
+
+from roundtrip import dedent, round_trip, round_trip_load # type: ignore
+
+
+def load(s: str, version: Optional[Any] = None) -> Any:
+ import ruamel.yaml # NOQA
+
+ yaml = ruamel.yaml.YAML()
+ yaml.version = version
+ return yaml.load(dedent(s))
+
+
+class TestVersions:
+ def test_explicit_1_2(self) -> None:
+ r = load("""\
+ %YAML 1.2
+ ---
+ - 12:34:56
+ - 012
+ - 012345678
+ - 0o12
+ - on
+ - off
+ - yes
+ - no
+ - true
+ """)
+ assert r[0] == '12:34:56'
+ assert r[1] == 12
+ assert r[2] == 12345678
+ assert r[3] == 10
+ assert r[4] == 'on'
+ assert r[5] == 'off'
+ assert r[6] == 'yes'
+ assert r[7] == 'no'
+ assert r[8] is True
+
+ def test_explicit_1_1(self) -> None:
+ r = load("""\
+ %YAML 1.1
+ ---
+ - 12:34:56
+ - 012
+ - 012345678
+ - 0o12
+ - on
+ - off
+ - yes
+ - no
+ - true
+ """)
+ assert r[0] == 45296
+ assert r[1] == 10
+ assert r[2] == '012345678'
+ assert r[3] == '0o12'
+ assert r[4] is True
+ assert r[5] is False
+ assert r[6] is True
+ assert r[7] is False
+ assert r[8] is True
+
+ def test_implicit_1_2(self) -> None:
+ r = load("""\
+ - 12:34:56
+ - 12:34:56.78
+ - 012
+ - 012345678
+ - 0o12
+ - on
+ - off
+ - yes
+ - no
+ - true
+ """)
+ assert r[0] == '12:34:56'
+ assert r[1] == '12:34:56.78'
+ assert r[2] == 12
+ assert r[3] == 12345678
+ assert r[4] == 10
+ assert r[5] == 'on'
+ assert r[6] == 'off'
+ assert r[7] == 'yes'
+ assert r[8] == 'no'
+ assert r[9] is True
+
+ def test_load_version_1_1(self) -> None:
+ inp = """\
+ - 12:34:56
+ - 12:34:56.78
+ - 012
+ - 012345678
+ - 0o12
+ - on
+ - off
+ - yes
+ - no
+ - true
+ """
+ r = load(inp, version='1.1')
+ assert r[0] == 45296
+ assert r[1] == 45296.78
+ assert r[2] == 10
+ assert r[3] == '012345678'
+ assert r[4] == '0o12'
+ assert r[5] is True
+ assert r[6] is False
+ assert r[7] is True
+ assert r[8] is False
+ assert r[9] is True
+
+
+class TestIssue62:
+ # bitbucket issue 62, issue_62
+ def test_00(self) -> None:
+ import ruamel.yaml # NOQA
+
+ s = dedent("""\
+ {}# Outside flow collection:
+ - ::vector
+ - ": - ()"
+ - Up, up, and away!
+ - -123
+ - http://example.com/foo#bar
+ # Inside flow collection:
+ - [::vector, ": - ()", "Down, down and away!", -456, http://example.com/foo#bar]
+ """)
+ with pytest.raises(ruamel.yaml.parser.ParserError):
+ round_trip(s.format('%YAML 1.1\n---\n'), preserve_quotes=True)
+ round_trip(s.format(""), preserve_quotes=True)
+
+ def test_00_single_comment(self) -> None:
+ import ruamel.yaml # NOQA
+
+ s = dedent("""\
+ {}# Outside flow collection:
+ - ::vector
+ - ": - ()"
+ - Up, up, and away!
+ - -123
+ - http://example.com/foo#bar
+ - [::vector, ": - ()", "Down, down and away!", -456, http://example.com/foo#bar]
+ """)
+ with pytest.raises(ruamel.yaml.parser.ParserError):
+ round_trip(s.format('%YAML 1.1\n---\n'), preserve_quotes=True)
+ round_trip(s.format(""), preserve_quotes=True)
+ # round_trip(s.format('%YAML 1.2\n---\n'), preserve_quotes=True, version=(1, 2))
+
+ def test_01(self) -> None:
+ import ruamel.yaml # NOQA
+
+ s = dedent("""\
+ {}[random plain value that contains a ? character]
+ """)
+ with pytest.raises(ruamel.yaml.parser.ParserError):
+ round_trip(s.format('%YAML 1.1\n---\n'), preserve_quotes=True)
+ round_trip(s.format(""), preserve_quotes=True)
+ # note the flow seq on the --- line!
+ round_trip(s.format('%YAML 1.2\n--- '), preserve_quotes=True, version='1.2')
+
+ def test_so_45681626(self) -> None:
+ # was not properly parsing
+ round_trip_load('{"in":{},"out":{}}')
+
+
+class TestVersionComparison:
+ def test_vc(self) -> None:
+ from ruamel.yaml.docinfo import Version
+
+ assert Version(1, 1) <= Version(2, 0)
+ assert Version(1, 1) <= Version(1, 2)
+ assert Version(1, 1) <= Version(1, 1)
+ assert Version(1, 3) == Version(1, 3)
+ assert Version(1, 2) > Version(1, 1)
+ assert Version(2, 0) > Version(1, 1)
+ assert Version(2, 0) >= Version(1, 1)
+ assert Version(1, 2) >= Version(1, 2)
diff --git a/_test/test_yamlfile.py b/_test/test_yamlfile.py
new file mode 100644
index 0000000..2fc8d7f
--- /dev/null
+++ b/_test/test_yamlfile.py
@@ -0,0 +1,233 @@
+# coding: utf-8
+
+"""
+various test cases for YAML files
+"""
+
+import sys
+import io
+import pytest # type: ignore # NOQA
+import platform
+
+from roundtrip import round_trip, dedent, round_trip_load, round_trip_dump # type: ignore # NOQA
+
+
+class TestYAML:
+ def test_backslash(self) -> None:
+ round_trip("""
+ handlers:
+ static_files: applications/\\1/static/\\2
+ """)
+
+ def test_omap_out(self) -> None:
+ # ordereddict mapped to !!omap
+ from ruamel.yaml.compat import ordereddict
+ import ruamel.yaml # NOQA
+
+ x = ordereddict([('a', 1), ('b', 2)])
+ res = round_trip_dump(x, default_flow_style=False)
+ assert res == dedent("""
+ !!omap
+ - a: 1
+ - b: 2
+ """)
+
+ def test_omap_roundtrip(self) -> None:
+ round_trip("""
+ !!omap
+ - a: 1
+ - b: 2
+ - c: 3
+ - d: 4
+ """)
+
+ # @pytest.mark.skipif(sys.version_info < (2, 7),
+ # reason='collections not available')
+ # def test_dump_collections_ordereddict(self) -> None:
+ # from collections import OrderedDict
+ # import ruamel.yaml # NOQA
+
+ # # OrderedDict mapped to !!omap
+ # x = OrderedDict([('a', 1), ('b', 2)])
+ # res = round_trip_dump(x, default_flow_style=False)
+ # assert res == dedent("""
+ # !!omap
+ # - a: 1
+ # - b: 2
+ # """)
+
+ @pytest.mark.skipif( # type: ignore
+ sys.version_info >= (3, 0) or platform.python_implementation() != 'CPython',
+ reason='ruamel.yaml not available',
+ )
+ def test_dump_ruamel_ordereddict(self) -> None:
+ from ruamel.ordereddict import ordereddict
+ import ruamel.yaml # NOQA
+
+ # OrderedDict mapped to !!omap
+ x = ordereddict([('a', 1), ('b', 2)])
+ res = round_trip_dump(x, default_flow_style=False)
+ assert res == dedent("""
+ !!omap
+ - a: 1
+ - b: 2
+ """)
+
+ def test_CommentedSet(self) -> None:
+ from ruamel.yaml.constructor import CommentedSet
+
+ s = CommentedSet(['a', 'b', 'c'])
+ s.remove('b')
+ s.add('d')
+ assert s == CommentedSet(['a', 'c', 'd'])
+ s.add('e')
+ s.add('f')
+ s.remove('e')
+ assert s == CommentedSet(['a', 'c', 'd', 'f'])
+
+ def test_set_out(self) -> None:
+ # preferable would be the shorter format without the ': null'
+ import ruamel.yaml # NOQA
+
+ x = set(['a', 'b', 'c']) # NOQA
+ # cannot use round_trip_dump, it doesn't show null in block style
+ buf = io.StringIO()
+ with pytest.warns(PendingDeprecationWarning):
+ yaml = ruamel.yaml.YAML(typ='unsafe', pure=True)
+ yaml.default_flow_style = False
+ yaml.dump(x, buf)
+ assert buf.getvalue() == dedent("""
+ !!set
+ a: null
+ b: null
+ c: null
+ """)
+
+ # ordering is not preserved in a set
+ def test_set_compact(self) -> None:
+ # this format is read and also should be written by default
+ round_trip("""
+ !!set
+ ? a
+ ? b
+ ? c
+ """)
+
+ def test_set_compact_flow(self) -> None:
+ # this format is read and also should be written by default
+ round_trip("""
+ !!set {a, b, c}
+ """)
+
+ def test_blank_line_after_comment(self) -> None:
+ round_trip("""
+ # Comment with spaces after it.
+
+
+ a: 1
+ """)
+
+ def test_blank_line_between_seq_items(self) -> None:
+ round_trip("""
+ # Seq with empty lines in between items.
+ b:
+ - bar
+
+
+ - baz
+ """)
+
+ @pytest.mark.skipif( # type: ignore
+ platform.python_implementation() == 'Jython', reason='Jython throws RepresenterError',
+ )
+ def test_blank_line_after_literal_chip(self) -> None:
+ s = """
+ c:
+ - |
+ This item
+ has a blank line
+ following it.
+
+ - |
+ To visually separate it from this item.
+
+ This item contains a blank line.
+
+
+ """
+ d = round_trip_load(dedent(s))
+ print(d)
+ round_trip(s)
+ assert d['c'][0].split('it.')[1] == '\n'
+ assert d['c'][1].split('line.')[1] == '\n'
+
+ @pytest.mark.skipif( # type: ignore
+ platform.python_implementation() == 'Jython', reason='Jython throws RepresenterError',
+ )
+ def test_blank_line_after_literal_keep(self) -> None:
+ """ have to insert an eof marker in YAML to test this"""
+ s = """
+ c:
+ - |+
+ This item
+ has a blank line
+ following it.
+
+ - |+
+ To visually separate it from this item.
+
+ This item contains a blank line.
+
+
+ ...
+ """
+ d = round_trip_load(dedent(s))
+ print(d)
+ round_trip(s)
+ assert d['c'][0].split('it.')[1] == '\n\n'
+ assert d['c'][1].split('line.')[1] == '\n\n\n'
+
+ @pytest.mark.skipif( # type: ignore
+ platform.python_implementation() == 'Jython', reason='Jython throws RepresenterError',
+ )
+ def test_blank_line_after_literal_strip(self) -> None:
+ s = """
+ c:
+ - |-
+ This item
+ has a blank line
+ following it.
+
+ - |-
+ To visually separate it from this item.
+
+ This item contains a blank line.
+
+
+ """
+ d = round_trip_load(dedent(s))
+ print(d)
+ round_trip(s)
+ assert d['c'][0].split('it.')[1] == ""
+ assert d['c'][1].split('line.')[1] == ""
+
+ def test_load_all_perserve_quotes(self) -> None:
+ import ruamel.yaml # NOQA
+
+ yaml = ruamel.yaml.YAML()
+ yaml.preserve_quotes = True
+ s = dedent("""\
+ a: 'hello'
+ ---
+ b: "goodbye"
+ """)
+ data = []
+ for x in yaml.load_all(s):
+ data.append(x)
+ buf = ruamel.yaml.compat.StringIO()
+ yaml.dump_all(data, buf)
+ out = buf.getvalue()
+ print(type(data[0]['a']), data[0]['a'])
+ # out = ruamel.yaml.round_trip_dump_all(data)
+ print(out)
+ assert out == s
diff --git a/_test/test_yamlobject.py b/_test/test_yamlobject.py
new file mode 100644
index 0000000..b5dd154
--- /dev/null
+++ b/_test/test_yamlobject.py
@@ -0,0 +1,92 @@
+# coding: utf-8
+
+import sys
+from typing import Any
+import pytest # type: ignore # NOQA
+
+from roundtrip import save_and_run # type: ignore # NOQA
+
+
+def test_monster(tmpdir: Any) -> None:
+ program_src = '''\
+ import ruamel.yaml
+ from textwrap import dedent
+
+ class Monster(ruamel.yaml.YAMLObject):
+ yaml_tag = '!Monster'
+
+ def __init__(self, name, hp, ac, attacks):
+ self.name = name
+ self.hp = hp
+ self.ac = ac
+ self.attacks = attacks
+
+ def __repr__(self):
+ return "%s(name=%r, hp=%r, ac=%r, attacks=%r)" % (
+ self.__class__.__name__, self.name, self.hp, self.ac, self.attacks)
+
+ yaml = ruamel.yaml.YAML(typ='safe', pure='True')
+ yaml = ruamel.yaml.YAML()
+ data = yaml.load(dedent("""\\
+ --- !Monster
+ name: Cave spider
+ hp: [2,6] # 2d6
+ ac: 16
+ attacks: [BITE, HURT]
+ """))
+ # normal dump, keys will be sorted
+ from io import BytesIO
+ buf = BytesIO()
+ yaml.dump(data, buf)
+ print(buf.getvalue().decode('utf-8'))
+ assert buf.getvalue().decode('utf8') == dedent("""\\
+ !Monster
+ name: Cave spider
+ hp: [2, 6] # 2d6
+ ac: 16
+ attacks: [BITE, HURT]
+ """)
+ '''
+ assert save_and_run(program_src, tmpdir) == 0
+
+
+@pytest.mark.skipif(sys.version_info < (3, 0), reason='no __qualname__') # type: ignore
+def test_qualified_name00(tmpdir: Any) -> None:
+ """issue 214"""
+ program_src = """\
+ from ruamel.yaml import YAML
+ from ruamel.yaml.compat import StringIO
+
+ class A:
+ def f(self):
+ pass
+
+ yaml = YAML(typ='unsafe', pure=True)
+ yaml.explicit_end = True
+ buf = StringIO()
+ yaml.dump(A.f, buf)
+ res = buf.getvalue()
+ print('res', repr(res))
+ assert res == "!!python/name:__main__.A.f ''\\n...\\n"
+ x = yaml.load(res)
+ assert x == A.f
+ """
+ assert save_and_run(program_src, tmpdir) == 0
+
+
+@pytest.mark.skipif(sys.version_info < (3, 0), reason='no __qualname__') # type: ignore
+def test_qualified_name01(tmpdir: Any) -> None:
+ """issue 214"""
+ from ruamel.yaml import YAML
+ import ruamel.yaml.comments
+ from ruamel.yaml.compat import StringIO
+
+ with pytest.warns(PendingDeprecationWarning):
+ yaml = YAML(typ='unsafe', pure=True)
+ yaml.explicit_end = True
+ buf = StringIO()
+ yaml.dump(ruamel.yaml.comments.CommentedBase.yaml_anchor, buf)
+ res = buf.getvalue()
+ assert res == "!!python/name:ruamel.yaml.comments.CommentedBase.yaml_anchor ''\n...\n"
+ x = yaml.load(res)
+ assert x == ruamel.yaml.comments.CommentedBase.yaml_anchor
diff --git a/_test/test_z_check_debug_leftovers.py b/_test/test_z_check_debug_leftovers.py
new file mode 100644
index 0000000..cd636aa
--- /dev/null
+++ b/_test/test_z_check_debug_leftovers.py
@@ -0,0 +1,36 @@
+# coding: utf-8
+
+import sys
+from typing import Any
+import pytest # type: ignore # NOQA
+
+from roundtrip import round_trip_load, round_trip_dump, dedent # type: ignore
+
+
+class TestLeftOverDebug:
+ # idea here is to capture round_trip_output via pytest stdout capture
+ # if there is are any leftover debug statements they should show up
+ def test_00(self, capsys: Any) -> None:
+ s = dedent("""
+ a: 1
+ b: []
+ c: [a, 1]
+ d: {f: 3.14, g: 42}
+ """)
+ d = round_trip_load(s)
+ round_trip_dump(d, sys.stdout)
+ out, err = capsys.readouterr()
+ assert out == s
+
+ def test_01(self, capsys: Any) -> None:
+ s = dedent("""
+ - 1
+ - []
+ - [a, 1]
+ - {f: 3.14, g: 42}
+ - - 123
+ """)
+ d = round_trip_load(s)
+ round_trip_dump(d, sys.stdout)
+ out, err = capsys.readouterr()
+ assert out == s
diff --git a/_test/test_z_data.py b/_test/test_z_data.py
new file mode 100644
index 0000000..1f31f4f
--- /dev/null
+++ b/_test/test_z_data.py
@@ -0,0 +1,404 @@
+# coding: utf-8
+
+import sys
+import os
+import pytest # type: ignore # NOQA
+import warnings # NOQA
+from typing import Any, Optional, List, Tuple
+from pathlib import Path
+
+base_path = Path('data') # that is ruamel.yaml.data
+
+
+class YAMLData:
+ yaml_tag = '!YAML'
+
+ def __init__(self, s: Any) -> None:
+ self._s = s
+
+ # Conversion tables for input. E.g. "<TAB>" is replaced by "\t"
+ # fmt: off
+ special = {
+ 'SPC': ' ',
+ 'TAB': '\t',
+ '---': '---',
+ '...': '...',
+ 'NL': '\n',
+ }
+ # fmt: on
+
+ @property
+ def value(self) -> Any:
+ if hasattr(self, '_p'):
+ return self._p # type: ignore
+ assert ' \n' not in self._s
+ assert '\t\n' not in self._s
+ self._p = self._s
+ for k, v in YAMLData.special.items():
+ k = '<' + k + '>'
+ self._p = self._p.replace(k, v)
+ return self._p
+
+ def test_rewrite(self, s: str) -> str:
+ assert ' \n' not in s
+ assert '\t\n' not in s
+ for k, v in YAMLData.special.items():
+ k = '<' + k + '>'
+ s = s.replace(k, v)
+ return s
+
+ @classmethod
+ def from_yaml(cls, constructor: Any, node: Any) -> 'YAMLData':
+ from ruamel.yaml.nodes import MappingNode
+
+ if isinstance(node, MappingNode):
+ return cls(constructor.construct_mapping(node))
+ return cls(node.value)
+
+
+class Python(YAMLData):
+ yaml_tag = '!Python'
+
+
+class Output(YAMLData):
+ yaml_tag = '!Output'
+
+
+class Assert(YAMLData):
+ yaml_tag = '!Assert'
+
+ @property
+ def value(self) -> Any:
+ from collections.abc import Mapping
+
+ if hasattr(self, '_pa'):
+ return self._pa # type: ignore
+ if isinstance(self._s, Mapping):
+ self._s['lines'] = self.test_rewrite(self._s['lines']) # type: ignore
+ self._pa = self._s
+ return self._pa
+
+
+class Events(YAMLData):
+ yaml_tag = '!Events'
+
+
+class JSONData(YAMLData):
+ yaml_tag = '!JSON'
+
+
+class Dump(YAMLData):
+ yaml_tag = '!Dump'
+
+
+class Emit(YAMLData):
+ yaml_tag = '!Emit'
+
+
+def pytest_generate_tests(metafunc: Any) -> None:
+ test_yaml = []
+ paths = sorted(base_path.glob('**/*.yaml'))
+ idlist = []
+ for path in paths:
+ # while developing tests put them in data/debug and run:
+ # auto -c "pytest _test/test_z_data.py" data/debug/*.yaml *.py _test/*.py
+ if os.environ.get('RUAMELAUTOTEST') == '1':
+ if path.parent.stem != 'debug':
+ continue
+ elif path.parent.stem == 'debug':
+ # don't test debug entries for production
+ continue
+ stem = path.stem
+ if stem.startswith('.#'): # skip emacs temporary file
+ continue
+ idlist.append(stem)
+ test_yaml.append([path])
+ metafunc.parametrize(['yaml'], test_yaml, ids=idlist, scope='class')
+
+
+class TestYAMLData:
+ def yaml(
+ self, yaml_version: Optional[Any] = None, typ: Any = 'rt', pure: Any = None,
+ ) -> Any:
+ from ruamel.yaml import YAML
+
+ y = YAML(typ=typ, pure=pure)
+ y.preserve_quotes = True
+ if yaml_version:
+ y.version = yaml_version
+ y.composer.warn_double_anchors = False
+ return y
+
+ def docs(self, path: Path) -> List[Any]:
+ from ruamel.yaml import YAML
+
+ tyaml = YAML(typ='safe', pure=True)
+ tyaml.register_class(YAMLData)
+ tyaml.register_class(Python)
+ tyaml.register_class(Output)
+ tyaml.register_class(Assert)
+ tyaml.register_class(Events)
+ tyaml.register_class(JSONData)
+ tyaml.register_class(Dump)
+ tyaml.register_class(Emit)
+ return list(tyaml.load_all(path))
+
+ def yaml_load(self, value: Any, yaml_version: Optional[Any] = None) -> Tuple[Any, Any]:
+ yaml = self.yaml(yaml_version=yaml_version)
+ data = yaml.load(value)
+ return yaml, data
+
+ def round_trip(
+ self, input: Any, output: Optional[Any] = None, yaml_version: Optional[Any] = None,
+ ) -> None:
+ from ruamel.yaml.compat import StringIO
+
+ yaml, data = self.yaml_load(input.value, yaml_version=yaml_version)
+ buf = StringIO()
+ yaml.dump(data, buf)
+ expected = input.value if output is None else output.value
+ value = buf.getvalue()
+ print('>>>> rt output\n', value.replace(' ', '\u2423'), sep='') # 2423 open box
+ assert value == expected
+
+ def gen_events(
+ self, input: Any, output: Any, yaml_version: Optional[Any] = None,
+ ) -> None:
+ from ruamel.yaml.compat import StringIO
+
+ buf = StringIO()
+ yaml = self.yaml(yaml_version=yaml_version)
+ indent = 0
+ try:
+ for event in yaml.parse(input.value):
+ compact = event.compact_repr()
+ assert compact[0] in '+=-'
+ if compact[0] == '-':
+ indent -= 1
+ print(f'{" "*indent}{compact}', file=buf)
+ if compact[0] == '+':
+ indent += 1
+
+ except Exception as e: # NOQA
+ print('=EXCEPTION', file=buf) # exceptions not indented
+ if '=EXCEPTION' not in output.value:
+ raise
+ print('>>>> buf\n', buf.getvalue(), sep='')
+ assert buf.getvalue() == output.value
+
+ def load_compare_json(
+ self, input: Any, output: Any, yaml_version: Optional[Any] = None,
+ ) -> None:
+ import json
+ from ruamel.yaml.compat import StringIO
+ from ruamel.yaml.comments import CommentedMap, TaggedScalar
+
+ def serialize_obj(obj: Any) -> Any:
+ if isinstance(obj, CommentedMap):
+ return {k: v for k, v in obj.items()} # NOQA
+ elif isinstance(obj, TaggedScalar):
+ return str(obj.value)
+ elif isinstance(obj, set):
+ return {k: None for k in obj}
+ return str(obj)
+
+ buf = StringIO()
+ yaml = self.yaml(typ='rt', yaml_version=yaml_version)
+ for data in yaml.load_all(input.value):
+ if isinstance(data, dict):
+ data = {str(k): v for k, v in data.items()}
+ json.dump(data, buf, sort_keys=True, indent=2, default=serialize_obj)
+ buf.write('\n')
+ print('>>>> buf\n', buf.getvalue(), sep='')
+ # jsons = json.dumps(json.loads(output.value)) # normalize formatting of JSON
+ assert buf.getvalue() == output.value
+
+ def load_compare_emit(
+ self, input: Any, output: Any, yaml_version: Optional[Any] = None,
+ ) -> None:
+ from ruamel.yaml.compat import StringIO
+
+ buf = StringIO()
+ yaml = self.yaml(yaml_version=yaml_version)
+ yaml.preserve_quotes = True
+ data = input.value
+ if data.startswith('---') or '\n--- ' in data or '\n---' in data:
+ yaml.explicit_start = True
+ data = list(yaml.load_all(data))
+ yaml.dump_all(data, buf)
+ print('>>>> buf\n', buf.getvalue(), sep='')
+ assert buf.getvalue() == output.value
+
+ def load_assert(
+ self, input: Any, confirm: Any, yaml_version: Optional[Any] = None,
+ ) -> None:
+ from collections.abc import Mapping
+
+ d = self.yaml_load(input.value, yaml_version=yaml_version)[1] # NOQA
+ print('confirm.value', confirm.value, type(confirm.value))
+ if isinstance(confirm.value, Mapping):
+ r = range(confirm.value['range'])
+ lines = confirm.value['lines'].splitlines()
+ for idx in r: # NOQA
+ for line in lines:
+ line = 'assert ' + line
+ print(line)
+ exec(line)
+ else:
+ for line in confirm.value.splitlines():
+ line = 'assert ' + line
+ print(line)
+ exec(line)
+
+ def run_python(
+ self, python: Any, data: Any, tmpdir: Any, input: Optional[Any] = None,
+ ) -> None:
+ from roundtrip import save_and_run # type: ignore
+
+ if input is not None:
+ (tmpdir / 'input.yaml').write_text(input.value, encoding='utf-8')
+ assert save_and_run(python.value, base_dir=tmpdir, output=data.value) == 0
+
+ def insert_comments(self, data: Any, actions: Any) -> None:
+ """this is to automatically insert based on:
+ path (a.1.b),
+ position (before, after, between), and
+ offset (absolute/relative)
+ """
+ raise NotImplementedError
+ expected = []
+ for line in data.value.splitlines(True):
+ idx = line.index['?']
+ if idx < 0:
+ expected.append(line)
+ continue
+ assert line.lstrip()[0] == '#' # it has to be comment line
+ print(data)
+ assert ''.join(expected) == data.value
+
+ # this is executed by pytest the methods with names not starting with
+ # test_ are helper methods
+ def test_yaml_data(self, yaml: Any, tmpdir: Any) -> None:
+ from collections.abc import Mapping
+
+ idx = 0
+ typs = [] # list of test to be performed
+ yaml_version = None
+
+ docs = self.docs(yaml)
+ if isinstance(docs[0], Mapping):
+ d = docs[0]
+ if d.get('skip'):
+ pytest.skip('explicit skip')
+ if '1.3-mod' in d.get('tags', []):
+ pytest.skip('YAML 1.3')
+ typ = d.get('type')
+ if isinstance(typ, str):
+ typs.append(typ)
+ elif isinstance(typ, list):
+ typs.extend(typ[:])
+ del typ
+ yaml_version = d.get('yaml_version')
+ if 'python' in d:
+ if not check_python_version(d['python']):
+ pytest.skip('unsupported version')
+ idx += 1
+ data = output = confirm = python = events = json = dump = emit = None
+ for doc in docs[idx:]:
+ if isinstance(doc, Output):
+ output = doc
+ elif isinstance(doc, Events):
+ events = doc
+ elif isinstance(doc, JSONData):
+ json = doc
+ elif isinstance(doc, Dump):
+ dump = doc # NOQA
+ elif isinstance(doc, Emit):
+ emit = doc # NOQA
+ elif isinstance(doc, Assert):
+ confirm = doc
+ elif isinstance(doc, Python):
+ python = doc
+ if len(typs) == 0:
+ typs = ['python_run']
+ elif isinstance(doc, YAMLData):
+ data = doc
+ else:
+ print('no handler for type:', type(doc), repr(doc))
+ raise AssertionError()
+ if len(typs) == 0:
+ if data is not None and output is not None:
+ typs = ['rt']
+ elif data is not None and confirm is not None:
+ typs = ['load_assert']
+ else:
+ assert data is not None
+ typs = ['rt']
+ print('type:', typs)
+ if data is not None:
+ print('>>>> data:\n', data.value.replace(' ', '\u2423'), sep='', end='')
+ if events is not None:
+ print('>>>> events:\n', events.value, sep='')
+ else:
+ print('>>>> output:\n', output.value if output is not None else output, sep='')
+ for typ in typs:
+ if typ == 'rt':
+ self.round_trip(data, output, yaml_version=yaml_version)
+ elif typ == 'python_run':
+ inp = None if output is None or data is None else data
+ self.run_python(
+ python, output if output is not None else data, tmpdir, input=inp,
+ )
+ elif typ == 'load_assert':
+ self.load_assert(data, confirm, yaml_version=yaml_version)
+ elif typ == 'comment':
+ actions: List[Any] = []
+ self.insert_comments(data, actions)
+ elif typ == 'events':
+ if events is None:
+ print('need to specify !Events for type:', typ)
+ sys.exit(1)
+ self.gen_events(data, events, yaml_version=yaml_version)
+ elif typ == 'json':
+ if json is None:
+ print('need to specify !JSON for type:', typ)
+ sys.exit(1)
+ self.load_compare_json(data, json, yaml_version=yaml_version)
+ elif typ == 'dump':
+ continue
+ elif typ == 'emit':
+ self.load_compare_emit(data, emit)
+ else:
+ f'\n>>>>>> run type unknown: "{typ}" <<<<<<\n'
+ raise AssertionError()
+
+
+def check_python_version(match: Any, current: Optional[Any] = None) -> bool:
+ """
+ version indication, return True if version matches.
+ match should be something like 3.6+, or [2.7, 3.3] etc. Floats
+ are converted to strings. Single values are made into lists.
+ """
+ if current is None:
+ current = list(sys.version_info[:3])
+ if not isinstance(match, list):
+ match = [match]
+ for m in match:
+ minimal = False
+ if isinstance(m, float):
+ m = str(m)
+ if m.endswith('+'):
+ minimal = True
+ m = m[:-1]
+ # assert m[0].isdigit()
+ # assert m[-1].isdigit()
+ m = [int(x) for x in m.split('.')]
+ current_len = current[: len(m)]
+ # print(m, current, current_len)
+ if minimal:
+ if current_len >= m:
+ return True
+ else:
+ if current_len == m:
+ return True
+ return False
diff --git a/_test/test_z_olddata.py b/_test/test_z_olddata.py
new file mode 100644
index 0000000..ffe1572
--- /dev/null
+++ b/_test/test_z_olddata.py
@@ -0,0 +1,42 @@
+# coding: utf-8
+
+import sys
+import os
+import pytest # type: ignore # NOQA
+
+sys.path.insert(0, os.path.dirname(__file__) + '/lib')
+
+import warnings # NOQA
+
+from typing import List, Any # NOQA
+
+args: List[Any] = []
+
+
+def test_data() -> None:
+ import test_appliance # type: ignore # NOQA
+
+ warnings.simplefilter('ignore', PendingDeprecationWarning)
+ collections = []
+ import test_yaml # type: ignore
+
+ collections.append(test_yaml)
+ test_appliance.run(collections, args)
+
+
+# @pytest.mark.skipif(not ruamel.yaml.__with_libyaml__,
+# reason="no libyaml")
+
+
+def test_data_ext() -> None:
+ collections = []
+ import ruamel.yaml # NOQA
+ import test_appliance # NOQA
+
+ warnings.simplefilter('ignore', ruamel.yaml.error.UnsafeLoaderWarning)
+ warnings.simplefilter('ignore', PendingDeprecationWarning)
+ if ruamel.yaml.__with_libyaml__:
+ import test_yaml_ext # type: ignore
+
+ collections.append(test_yaml_ext)
+ test_appliance.run(collections, args)
diff --git a/anchor.py b/anchor.py
index 1eb1480..61119af 100644
--- a/anchor.py
+++ b/anchor.py
@@ -1,6 +1,8 @@
-# coding: utf-8
-from typing import Any, Dict, Optional, List, Union, Optional, Iterator # NOQA
+from __future__ import annotations
+
+if False: # MYPY
+ from typing import Any, Dict, Optional, List, Union, Optional, Iterator # NOQA
anchor_attrib = '_yaml_anchor'
diff --git a/comments.py b/comments.py
index 843b329..9a23135 100644
--- a/comments.py
+++ b/comments.py
@@ -1,4 +1,5 @@
-# coding: utf-8
+
+from __future__ import annotations
"""
stuff to deal with comments and formatting on dict/list/ordereddict/set
@@ -18,7 +19,8 @@ from ruamel.yaml.tag import Tag
from collections.abc import MutableSet, Sized, Set, Mapping
-from typing import Any, Dict, Optional, List, Union, Optional, Iterator # NOQA
+if False: # MYPY
+ from typing import Any, Dict, Optional, List, Union, Optional, Iterator # NOQA
# fmt: off
__all__ = ['CommentedSeq', 'CommentedKeySeq',
diff --git a/compat.py b/compat.py
index 9786fae..46d0c3b 100644
--- a/compat.py
+++ b/compat.py
@@ -1,4 +1,3 @@
-# coding: utf-8
from __future__ import annotations
@@ -7,18 +6,23 @@ from __future__ import annotations
import sys
import os
import io
-import traceback
from abc import abstractmethod
import collections.abc
+from ruamel.yaml.docinfo import Version # NOQA
# fmt: off
-from typing import Any, Dict, Optional, List, Union, BinaryIO, IO, Text, Tuple # NOQA
-from typing import Optional # NOQA
-try:
- from typing import SupportsIndex as SupportsIndex # in order to reexport for mypy
-except ImportError:
- SupportsIndex = int # type: ignore
+if False: # MYPY
+ from typing import Any, Dict, Optional, List, Union, BinaryIO, IO, Text, Tuple # NOQA
+ from typing import Optional # NOQA
+ try:
+ from typing import SupportsIndex as SupportsIndex # in order to reexport for mypy
+ except ImportError:
+ SupportsIndex = int # type: ignore
+
+ StreamType = Any
+ StreamTextType = StreamType
+ VersionType = Union[str , Tuple[int, int] , List[int] , Version , None]
# fmt: on
_DEFAULT_YAML_VERSION = (1, 2)
@@ -51,11 +55,6 @@ class ordereddict(OrderedDict): # type: ignore
StringIO = io.StringIO
BytesIO = io.BytesIO
-StreamType = Any
-
-StreamTextType = StreamType
-from ruamel.yaml.docinfo import Version # NOQA
-VersionType = Union[str , Tuple[int, int] , List[int] , Version , None]
builtins_module = 'builtins'
@@ -117,6 +116,8 @@ class Nprint:
self._file_name = file_name
def __call__(self, *args: Any, **kw: Any) -> None:
+ import traceback
+
if not bool(_debug):
return
out = sys.stdout if self._file_name is None else open(self._file_name, 'a')
diff --git a/composer.py b/composer.py
index 3802d94..ca4031a 100644
--- a/composer.py
+++ b/composer.py
@@ -1,4 +1,5 @@
-# coding: utf-8
+
+from __future__ import annotations
import warnings
@@ -17,7 +18,8 @@ from ruamel.yaml.events import (
)
from ruamel.yaml.nodes import MappingNode, ScalarNode, SequenceNode
-from typing import Any, Dict, Optional, List # NOQA
+if False: # MYPY
+ from typing import Any, Dict, Optional, List # NOQA
__all__ = ['Composer', 'ComposerError']
diff --git a/configobjwalker.py b/configobjwalker.py
index 28318f1..236666c 100644
--- a/configobjwalker.py
+++ b/configobjwalker.py
@@ -1,10 +1,12 @@
-# coding: utf-8
+
+from __future__ import annotations
import warnings
from ruamel.yaml.util import configobj_walker as new_configobj_walker
-from typing import Any
+if False: # MYPY
+ from typing import Any
def configobj_walker(cfg: Any) -> Any:
diff --git a/constructor.py b/constructor.py
index e4f6f16..a25ef20 100644
--- a/constructor.py
+++ b/constructor.py
@@ -1,7 +1,8 @@
-# coding: utf-8
+
+from __future__ import annotations
import datetime
-import base64
+from datetime import timedelta as TimeDelta
import binascii
import sys
import types
@@ -34,7 +35,8 @@ from ruamel.yaml.scalarbool import ScalarBoolean
from ruamel.yaml.timestamp import TimeStamp
from ruamel.yaml.util import timestamp_regexp, create_timestamp
-from typing import Any, Dict, List, Set, Iterator, Union, Optional # NOQA
+if False: # MYPY
+ from typing import Any, Dict, List, Set, Iterator, Union, Optional # NOQA
__all__ = ['BaseConstructor', 'SafeConstructor', 'Constructor',
@@ -511,6 +513,8 @@ class SafeConstructor(BaseConstructor):
return sign * float(value_s)
def construct_yaml_binary(self, node: Any) -> Any:
+ import base64
+
try:
value = self.construct_scalar(node).encode('ascii')
except UnicodeEncodeError as exc:
@@ -662,6 +666,8 @@ class Constructor(SafeConstructor):
return self.construct_scalar(node)
def construct_python_bytes(self, node: Any) -> Any:
+ import base64
+
try:
value = self.construct_scalar(node).encode('ascii')
except UnicodeEncodeError as exc:
@@ -1487,14 +1493,17 @@ class RoundTripConstructor(SafeConstructor):
state = SafeConstructor.construct_mapping(self, node, deep=True)
data.__setstate__(state)
elif is_dataclass(data):
- mapping = SafeConstructor.construct_mapping(self, node)
+ mapping = SafeConstructor.construct_mapping(self, node, deep=True)
init_var_defaults = {}
for field in data.__dataclass_fields__.values():
# nprintf('field', field, field.default is MISSING,
# isinstance(field.type, InitVar))
# in 3.7, InitVar is a singleton
if (
- isinstance(field.type, InitVar) or field.type is InitVar
+ isinstance(field.type, InitVar)
+ or field.type is InitVar
+ # this following is for handling from __future__ import allocations
+ or (isinstance(field.type, str) and field.type.startswith('InitVar'))
) and field.default is not MISSING:
init_var_defaults[field.name] = field.default
for attr, value in mapping.items():
@@ -1676,20 +1685,21 @@ class RoundTripConstructor(SafeConstructor):
else:
return create_timestamp(**values)
# return SafeConstructor.construct_yaml_timestamp(self, node, values)
- dd = create_timestamp(**values) # this has delta applied
+ # print('>>>>>>>> here', values)
+ dd = create_timestamp(**values) # this has tzinfo
delta = None
if values['tz_sign']:
- tz_hour = int(values['tz_hour'])
+ hours = values['tz_hour']
+ tz_hour = int(hours)
minutes = values['tz_minute']
tz_minute = int(minutes) if minutes else 0
- delta = datetime.timedelta(hours=tz_hour, minutes=tz_minute)
+ # ToDo: double work, replace with extraction from dd.tzinfo
+ delta = TimeDelta(hours=tz_hour, minutes=tz_minute)
if values['tz_sign'] == '-':
delta = -delta
- # should check for None and solve issue 366 should be tzinfo=delta)
- # isinstance(datetime.datetime.now, datetime.date) is true)
if isinstance(dd, datetime.datetime):
data = TimeStamp(
- dd.year, dd.month, dd.day, dd.hour, dd.minute, dd.second, dd.microsecond,
+ dd.year, dd.month, dd.day, dd.hour, dd.minute, dd.second, dd.microsecond, dd.tzinfo, # NOQA
)
else:
# ToDo: make this into a DateStamp?
diff --git a/cyaml.py b/cyaml.py
index 3f15ffc..54bd71e 100644
--- a/cyaml.py
+++ b/cyaml.py
@@ -1,4 +1,5 @@
-# coding: utf-8
+
+from __future__ import annotations
from _ruamel_yaml import CParser, CEmitter # type: ignore
@@ -7,8 +8,9 @@ from ruamel.yaml.representer import Representer, SafeRepresenter, BaseRepresente
from ruamel.yaml.resolver import Resolver, BaseResolver
-from typing import Any, Union, Optional # NOQA
-from ruamel.yaml.compat import StreamTextType, StreamType, VersionType # NOQA
+if False: # MYPY
+ from typing import Any, Union, Optional # NOQA
+ from ruamel.yaml.compat import StreamTextType, StreamType, VersionType # NOQA
__all__ = ['CBaseLoader', 'CSafeLoader', 'CLoader', 'CBaseDumper', 'CSafeDumper', 'CDumper']
diff --git a/docinfo.py b/docinfo.py
index aec6ea7..1c9254b 100644
--- a/docinfo.py
+++ b/docinfo.py
@@ -15,17 +15,60 @@ DocInfo can be used by a yaml dumper to dump a class
- if provided to the dumper?
"""
-from typing import Optional, Tuple
-from dataclasses import dataclass, field, MISSING # NOQA
+if False: # MYPY
+ from typing import Optional, Tuple, Any
+# from dataclasses import dataclass, field, MISSING # NOQA
-@dataclass(order=True, frozen=True)
+
+# @dataclass(order=True, frozen=True)
class Version:
- major: int
- minor: int
+ # major: int
+ # minor: int
+ def __init__(self, major: int, minor: int) -> None:
+ self._major = major
+ self._minor = minor
+
+ @property
+ def major(self) -> int:
+ return self._major
+
+ @property
+ def minor(self) -> int:
+ return self._minor
+
+ def __eq__(self, v: Any) -> bool:
+ if not isinstance(v, Version):
+ return False
+ return self._major == v._major and self._minor == v._minor
+
+ def __lt__(self, v: Version) -> bool:
+ if self._major < v._major:
+ return True
+ if self._major > v._major:
+ return False
+ return self._minor < v._minor
- # def __repr__(self):
- # return f'Version("{self.major}.{self.minor}")'
+ def __le__(self, v: Version) -> bool:
+ if self._major < v._major:
+ return True
+ if self._major > v._major:
+ return False
+ return self._minor <= v._minor
+
+ def __gt__(self, v: Version) -> bool:
+ if self._major > v._major:
+ return True
+ if self._major < v._major:
+ return False
+ return self._minor > v._minor
+
+ def __ge__(self, v: Version) -> bool:
+ if self._major > v._major:
+ return True
+ if self._major < v._major:
+ return False
+ return self._minor >= v._minor
def version(
@@ -48,13 +91,24 @@ def version(
return Version(major, minor)
-@dataclass(frozen=True)
+# @dataclass(frozen=True)
class Tag:
- handle: str
- prefix: str
+ # handle: str
+ # prefix: str
+ def __init__(self, handle: str, prefix: str) -> None:
+ self._handle = handle
+ self._prefix = prefix
+
+ @property
+ def handle(self) -> str:
+ return self._handle
+
+ @property
+ def prefix(self) -> str:
+ return self._prefix
-@dataclass
+# @dataclass
class DocInfo:
"""
Store document information, can be used for analysis of a loaded YAML document
@@ -62,6 +116,15 @@ class DocInfo:
doc_version: from %YAML directive
tags: from %TAG directives in scanned order
"""
- requested_version: Optional[Version] = None
- doc_version: Optional[Version] = None
- tags: list[Tag] = field(default_factory=list)
+ # requested_version: Optional[Version] = None
+ # doc_version: Optional[Version] = None
+ # tags: list[Tag] = field(default_factory=list)
+ def __init__(
+ self,
+ requested_version: Optional[Version] = None,
+ doc_version: Optional[Version] = None,
+ tags: Optional[list[Tag]] = None,
+ ):
+ self.requested_version = requested_version
+ self.doc_version = doc_version
+ self.tags = [] if tags is None else tags
diff --git a/dumper.py b/dumper.py
index e6457a6..ece9429 100644
--- a/dumper.py
+++ b/dumper.py
@@ -1,4 +1,5 @@
-# coding: utf-8
+
+from __future__ import annotations
from ruamel.yaml.emitter import Emitter
from ruamel.yaml.serializer import Serializer
@@ -10,8 +11,9 @@ from ruamel.yaml.representer import (
)
from ruamel.yaml.resolver import Resolver, BaseResolver, VersionedResolver
-from typing import Any, Dict, List, Union, Optional # NOQA
-from ruamel.yaml.compat import StreamType, VersionType # NOQA
+if False: # MYPY
+ from typing import Any, Dict, List, Union, Optional # NOQA
+ from ruamel.yaml.compat import StreamType, VersionType # NOQA
__all__ = ['BaseDumper', 'SafeDumper', 'Dumper', 'RoundTripDumper']
diff --git a/emitter.py b/emitter.py
index 1d58be2..9352a16 100644
--- a/emitter.py
+++ b/emitter.py
@@ -1,4 +1,5 @@
-# coding: utf-8
+
+from __future__ import annotations
# Emitter expects events obeying the following grammar:
# stream ::= STREAM-START document* STREAM-END
@@ -17,8 +18,9 @@ from ruamel.yaml.compat import nprint, dbg, DBG_EVENT, \
# fmt: on
-from typing import Any, Dict, List, Union, Text, Tuple, Optional # NOQA
-from ruamel.yaml.compat import StreamType # NOQA
+if False: # MYPY
+ from typing import Any, Dict, List, Union, Text, Tuple, Optional # NOQA
+ from ruamel.yaml.compat import StreamType # NOQA
__all__ = ['Emitter', 'EmitterError']
diff --git a/error.py b/error.py
index 4843fdb..30116d1 100644
--- a/error.py
+++ b/error.py
@@ -1,9 +1,11 @@
-# coding: utf-8
+
+from __future__ import annotations
import warnings
-import textwrap
+# import textwrap
-from typing import Any, Dict, Optional, List, Text # NOQA
+if False: # MYPY
+ from typing import Any, Dict, Optional, List, Text # NOQA
__all__ = [
@@ -133,7 +135,7 @@ class MarkedYAMLError(YAMLError):
# warn is ignored
def __str__(self) -> Any:
- lines: List[str] = []
+ lines: list[str] = []
if self.context is not None:
lines.append(self.context)
if self.context_mark is not None and (
@@ -148,11 +150,20 @@ class MarkedYAMLError(YAMLError):
lines.append(self.problem)
if self.problem_mark is not None:
lines.append(str(self.problem_mark))
- if self.note is not None and self.note:
- note = textwrap.dedent(self.note)
- lines.append(note)
+ # if self.note is not None and self.note:
+ # note = textwrap.dedent(self.note)
+ # lines.append(note)
+ self.check_append(lines, self.note)
return '\n'.join(lines)
+ def check_append(self, lines: list[str], val: Optional[str]) -> None:
+ if val is None or not val:
+ return
+ import textwrap
+
+ note = textwrap.dedent(val)
+ lines.append(note)
+
class YAMLStreamError(Exception):
pass
@@ -195,14 +206,24 @@ class MarkedYAMLWarning(YAMLWarning):
lines.append(self.problem)
if self.problem_mark is not None:
lines.append(str(self.problem_mark))
- if self.note is not None and self.note:
- note = textwrap.dedent(self.note)
- lines.append(note)
- if self.warn is not None and self.warn:
- warn = textwrap.dedent(self.warn)
- lines.append(warn)
+ # if self.note is not None and self.note:
+ # note = textwrap.dedent(self.note)
+ # lines.append(note)
+ self.check_append(lines, self.note)
+ # if self.warn is not None and self.warn:
+ # warn = textwrap.dedent(self.warn)
+ # lines.append(warn)
+ self.check_append(lines, self.warn)
return '\n'.join(lines)
+ def check_append(self, lines: list[str], val: Optional[str]) -> None:
+ if val is None or not val:
+ return
+ import textwrap
+
+ note = textwrap.dedent(val)
+ lines.append(note)
+
class ReusedAnchorWarning(YAMLWarning):
pass
@@ -288,10 +309,20 @@ class MarkedYAMLFutureWarning(YAMLFutureWarning):
lines.append(self.problem)
if self.problem_mark is not None:
lines.append(str(self.problem_mark))
- if self.note is not None and self.note:
- note = textwrap.dedent(self.note)
- lines.append(note)
- if self.warn is not None and self.warn:
- warn = textwrap.dedent(self.warn)
- lines.append(warn)
+ # if self.note is not None and self.note:
+ # note = textwrap.dedent(self.note)
+ # lines.append(note)
+ self.check_append(lines, self.note)
+ # if self.warn is not None and self.warn:
+ # warn = textwrap.dedent(self.warn)
+ # lines.append(warn)
+ self.check_append(lines, self.warn)
return '\n'.join(lines)
+
+ def check_append(self, lines: list[str], val: Optional[str]) -> None:
+ if val is None or not val:
+ return
+ import textwrap
+
+ note = textwrap.dedent(val)
+ lines.append(note)
diff --git a/events.py b/events.py
index a570a0d..42af96c 100644
--- a/events.py
+++ b/events.py
@@ -1,8 +1,10 @@
-# coding: utf-8
+
+from __future__ import annotations
# Abstract classes.
-from typing import Any, Dict, Optional, List # NOQA
+if False: # MYPY
+ from typing import Any, Dict, Optional, List # NOQA
from ruamel.yaml.tag import Tag
SHOW_LINES = False
diff --git a/loader.py b/loader.py
index d6c708b..8406856 100644
--- a/loader.py
+++ b/loader.py
@@ -1,4 +1,5 @@
-# coding: utf-8
+
+from __future__ import annotations
from ruamel.yaml.reader import Reader
from ruamel.yaml.scanner import Scanner, RoundTripScanner
@@ -12,8 +13,9 @@ from ruamel.yaml.constructor import (
)
from ruamel.yaml.resolver import VersionedResolver
-from typing import Any, Dict, List, Union, Optional # NOQA
-from ruamel.yaml.compat import StreamTextType, VersionType # NOQA
+if False: # MYPY
+ from typing import Any, Dict, List, Union, Optional # NOQA
+ from ruamel.yaml.compat import StreamTextType, VersionType # NOQA
__all__ = ['BaseLoader', 'SafeLoader', 'Loader', 'RoundTripLoader']
diff --git a/main.py b/main.py
index b80c1f5..acef4bd 100644
--- a/main.py
+++ b/main.py
@@ -1,4 +1,3 @@
-# coding: utf-8
from __future__ import annotations
@@ -36,10 +35,11 @@ from ruamel.yaml.loader import Loader as UnsafeLoader # NOQA
from ruamel.yaml.comments import CommentedMap, CommentedSeq, C_PRE
from ruamel.yaml.docinfo import DocInfo, version, Version
-from typing import List, Set, Dict, Tuple, Union, Any, Callable, Optional, Text, Type # NOQA
-from types import TracebackType
-from ruamel.yaml.compat import StreamType, StreamTextType, VersionType # NOQA
-from pathlib import Path # NOQA
+if False: # MYPY
+ from typing import List, Set, Dict, Tuple, Union, Any, Callable, Optional, Text, Type # NOQA
+ from ruamel.yaml.compat import StreamType, StreamTextType, VersionType # NOQA
+ from types import TracebackType
+ from pathlib import Path
try:
from _ruamel_yaml import CParser, CEmitter # type: ignore
diff --git a/nodes.py b/nodes.py
index 1721049..f870167 100644
--- a/nodes.py
+++ b/nodes.py
@@ -1,8 +1,10 @@
-# coding: utf-8
+
+from __future__ import annotations
import sys
-from typing import Dict, Any, Text, Optional # NOQA
+if False: # MYPY
+ from typing import Dict, Any, Text, Optional # NOQA
from ruamel.yaml.tag import Tag
diff --git a/parser.py b/parser.py
index b031aa6..8f56195 100644
--- a/parser.py
+++ b/parser.py
@@ -1,4 +1,5 @@
-# coding: utf-8
+
+from __future__ import annotations
# The following YAML grammar is LL(1) and is parsed by a recursive descent
# parser.
@@ -83,7 +84,8 @@ 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
+if False: # MYPY
+ from typing import Any, Dict, Optional, List, Optional # NOQA
__all__ = ['Parser', 'RoundTripParser', 'ParserError']
diff --git a/pyproject.toml b/pyproject.toml
deleted file mode 100644
index 7e66379..0000000
--- a/pyproject.toml
+++ /dev/null
@@ -1,4 +0,0 @@
-[build-system]
-requires = ["setuptools", "wheel"]
-# test
-build-backend = "setuptools.build_meta"
diff --git a/reader.py b/reader.py
index 3780a2c..ef0f777 100644
--- a/reader.py
+++ b/reader.py
@@ -1,4 +1,5 @@
-# coding: utf-8
+
+from __future__ import annotations
# This module contains abstractions for the input stream. You don't have to
# looks further, there are no pretty code.
@@ -24,7 +25,8 @@ import codecs
from ruamel.yaml.error import YAMLError, FileMark, StringMark, YAMLStreamError
from ruamel.yaml.util import RegExp
-from typing import Any, Dict, Optional, List, Union, Text, Tuple, Optional # NOQA
+if False: # MYPY
+ from typing import Any, Dict, Optional, List, Union, Text, Tuple, Optional # NOQA
# from ruamel.yaml.compat import StreamTextType # NOQA
__all__ = ['Reader', 'ReaderError']
diff --git a/representer.py b/representer.py
index 0d1ca12..e3f492e 100644
--- a/representer.py
+++ b/representer.py
@@ -1,4 +1,5 @@
-# coding: utf-8
+
+from __future__ import annotations
from ruamel.yaml.error import * # NOQA
from ruamel.yaml.nodes import * # NOQA
@@ -35,7 +36,8 @@ import types
import copyreg
import base64
-from typing import Dict, List, Any, Union, Text, Optional # NOQA
+if False: # MYPY
+ from typing import Dict, List, Any, Union, Text, Optional # NOQA
# fmt: off
__all__ = ['BaseRepresenter', 'SafeRepresenter', 'Representer',
@@ -1024,13 +1026,16 @@ class RoundTripRepresenter(SafeRepresenter):
def represent_datetime(self, data: Any) -> ScalarNode:
inter = 'T' if data._yaml['t'] else ' '
_yaml = data._yaml
- if _yaml['delta']:
+ if False and _yaml['delta']:
data += _yaml['delta']
value = data.isoformat(inter)
else:
- value = data.isoformat(inter)
- if _yaml['tz']:
+ value = data.isoformat(inter).strip()
+ if False and _yaml['tz']:
value += _yaml['tz']
+ if data.tzinfo and str(data.tzinfo):
+ if value[-6] in '+-':
+ value = value[:-6] + str(data.tzinfo)
return self.represent_scalar('tag:yaml.org,2002:timestamp', value)
def represent_tagged_scalar(self, data: Any) -> ScalarNode:
diff --git a/resolver.py b/resolver.py
index aa3ca11..d693b8e 100644
--- a/resolver.py
+++ b/resolver.py
@@ -1,9 +1,11 @@
-# coding: utf-8
+
+from __future__ import annotations
import re
-from typing import Any, Dict, List, Union, Text, Optional # NOQA
-from ruamel.yaml.compat import VersionType # NOQA
+if False: # MYPY
+ 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
diff --git a/ruamel.yaml.egg-info/PKG-INFO b/ruamel.yaml.egg-info/PKG-INFO
deleted file mode 100644
index b4e9d9e..0000000
--- a/ruamel.yaml.egg-info/PKG-INFO
+++ /dev/null
@@ -1,430 +0,0 @@
-Metadata-Version: 2.1
-Name: ruamel.yaml
-Version: 0.18.5
-Summary: ruamel.yaml is a YAML parser/emitter that supports roundtrip preservation of comments, seq/map flow style, and map key order
-Author: Anthon van der Neut
-Author-email: a.van.der.neut@ruamel.eu
-License: MIT license
-Project-URL: Home, https://sourceforge.net/p/ruamel-yaml/
-Project-URL: Source, https://sourceforge.net/p/ruamel-yaml/code/ci/default/tree/
-Project-URL: Tracker, https://sourceforge.net/p/ruamel-yaml/tickets/
-Project-URL: Documentation, https://yaml.readthedocs.io/
-Keywords: yaml 1.2 parser round-trip preserve quotes order config
-Classifier: Development Status :: 4 - Beta
-Classifier: Intended Audience :: Developers
-Classifier: License :: OSI Approved :: MIT License
-Classifier: Operating System :: OS Independent
-Classifier: Programming Language :: Python
-Classifier: Programming Language :: Python :: 3 :: Only
-Classifier: Programming Language :: Python :: 3.10
-Classifier: Programming Language :: Python :: 3.11
-Classifier: Programming Language :: Python :: 3.12
-Classifier: Programming Language :: Python :: 3.7
-Classifier: Programming Language :: Python :: 3.8
-Classifier: Programming Language :: Python :: 3.9
-Classifier: Programming Language :: Python :: Implementation :: CPython
-Classifier: Topic :: Software Development :: Libraries :: Python Modules
-Classifier: Topic :: Text Processing :: Markup
-Classifier: Typing :: Typed
-Requires-Python: >=3.7
-Description-Content-Type: text/markdown; charset=UTF-8; variant=CommonMark
-Provides-Extra: jinja2
-Provides-Extra: docs
-License-File: LICENSE
-
-# ruamel.yaml
-
-`ruamel.yaml` is a YAML 1.2 loader/dumper package for Python.
-<table class="docutils">
- <tr> <td>version</td>
- <td>0.18.5</td>
- </tr>
- <tr> <td>updated</td>
- <td>2023-11-03</td>
- </tr>
- <tr> <td>documentation</td>
- <td><a href="https://yaml.readthedocs.io">https://yaml.readthedocs.io</a></td>
- </tr>
- <tr> <td>repository</td>
- <td><a href="https://sourceforge.net/projects/ruamel-yaml">https://sourceforge.net/projects/ruamel-yaml</a></td>
- </tr>
- <tr> <td>pypi</td>
- <td><a href="https://pypi.org/project/ruamel.yaml">https://pypi.org/project/ruamel.yaml</a></td>
- </tr>
-</table>
-
-As announced, in 0.18.0, the old PyYAML functions have been deprecated.
-(`scan`, `parse`, `compose`, `load`, `emit`, `serialize`, `dump` and their variants
-(`_all`, `safe_`, `round_trip_`, etc)). If you only read this after your program has
-stopped working: I am sorry to hear that, but that also means you, or the person
-developing your program, has not tested with warnings on (which is the recommendation
-in PEP 565, and e.g. defaultin when using `pytest`). If you have troubles, explicitly use
-```
-pip install "ruamel.yaml<0.18.0"
-```
-or put something to that effects in your requirments, to give yourself
-some time to solve the issue.
-
-There will be at least one more potentially breaking change in the 0.18 series: `YAML(typ='unsafe')`
-now has a pending deprecation warning and is going to be deprecated, probably before the end of 2023.
-If you only use it to dump, please use the new `YAML(typ='full')`, the result of that can be *safely*
-loaded with a default instance `YAML()`, as that will get you inspectable, tagged, scalars, instead of
-executed Python functions/classes. (You should probably add constructors for what you actually need,
-but I do consider adding a `ruamel.yaml.unsafe` package that will re-add the `typ='unsafe'` option.
-*Please adjust/pin your dependencies accordingly if necessary.*
-
-
-There seems to be a CVE on `ruamel.yaml`, stating that the `load()` function could be abused
-because of unchecked input. `load()` was never the default function (that was `round_trip_load()`
-before the new API came into existence`. So the creator of that CVE was ill informed and
-probably lazily assumed that since `ruamel.yaml` is a derivative of PyYAML (for which
-a similar CVE exists), the same problem would still exist, without checking.
-So the CVE was always inappriate, now just more so, as the call
-to the function `load()` with any input will terminate your program with an error message. If you
-(have to) care about such things as this CVE, my recommendation is to stop using Python
-completely, as `pickle.load()` can be abused in the same way as `load()` (and like unlike `load()`
-is only documented to be unsafe, without development-time warning.
-
-Version 0.17.21 was the last one tested to be working on Python 3.5 and 3.6<BR>
-The 0.16.13 release was the last that was tested to be working on Python 2.7.
-
-
-There are two extra plug-in packages
-(`ruamel.yaml.bytes` and `ruamel.yaml.string`)
-for those not wanting to do the streaming to a
-`io.BytesIO/StringIO` buffer themselves.
-
-If your package uses `ruamel.yaml` and is not listed on PyPI, drop me an
-email, preferably with some information on how you use the package (or a
-link to the repository) and I'll keep you informed when the status of
-the API is stable enough to make the transition.
-
-<pre>
- <a href="overview/#overview">Overview</a>
-
- <a href="install/#installing">Installing</a>
- <a href="install/#optional-requirements">Optional requirements</a>
-
- <a href="basicuse/#basic-usage">Basic Usage</a>
- <a href="basicuse/#load-and-dump">Load and dump </a>
- <a href="basicuse/#more-examples">More examples</a>
-
- <a href="dumpcls/#working-with-python-classes">Working with Python classes</a>
- <a href="dumpcls/#dumping-python-classes">Dumping Python classes</a>
- <a href="dumpcls/#dataclass">Dataclass</a>
-
- <a href="detail/#details">Details</a>
- <a href="detail/#indentation-of-block-sequences">Indentation of block sequences</a>
- <a href="detail/#inconsistently-indented-yaml">Inconsistently indented YAML</a>
- <a href="detail/#indenting-using-typsafe">Indenting using `typ="safe"`</a>
- <a href="detail/#positioning-in-top-level-mappings-prefixing">Positioning ':' in top level mappings, prefixing ':'</a>
- <a href="detail/#document-version-support">Document version support</a>
- <a href="detail/#round-trip-including-comments">Round trip including comments</a>
- <a href="detail/#config-file-formats">Config file formats</a>
- <a href="detail/#extending">Extending</a>
- <a href="detail/#smartening">Smartening</a>
-
- <a href="example/#examples">Examples</a>
- <a href="example/#output-of-dump-as-a-string">Output of `dump()` as a string</a>
-
- <a href="api/#departure-from-previous-api">Departure from previous API</a>
- <a href="api/#loading">Loading</a>
- <a href="api/#duplicate-keys">Duplicate keys</a>
- <a href="api/#dumping-a-multi-document-yaml-stream">Dumping a multi-document YAML stream</a>
- <a href="api/#dumping">Dumping</a>
- <a href="api/#controls">Controls</a>
- <a href="api/#transparent-usage-of-new-and-old-api">Transparent usage of new and old API</a>
- <a href="api/#reason-for-api-change">Reason for API change</a>
-
- <a href="pyyaml/#differences-with-pyyaml">Differences with PyYAML</a>
- <a href="pyyaml/#defaulting-to-yaml-12-support">Defaulting to YAML 1.2 support</a>
- <a href="pyyaml/#py2py3-reintegration">PY2/PY3 reintegration</a>
- <a href="pyyaml/#fixes">Fixes</a>
- <a href="pyyaml/#testing">Testing</a>
- <a href="pyyaml/#api">API</a>
-
- <a href="contributing/#contributing">Contributing</a>
- <a href="contributing/#documentation">Documentation</a>
- <a href="contributing/#code">Code</a>
- <a href="contributing/#flake">Flake</a>
- <a href="contributing/#toxpytest">Tox/pytest</a>
- <a href="contributing/#typingmypy">Typing/mypy</a>
- <a href="contributing/#generated-files">Generated files</a>
- <a href="contributing/#vulnerabilities">Vulnerabilities</a>
-</pre>
-
-
-[![image](https://readthedocs.org/projects/yaml/badge/?version=latest)](https://yaml.readthedocs.org/en/latest?badge=latest)[![image](https://bestpractices.coreinfrastructure.org/projects/1128/badge)](https://bestpractices.coreinfrastructure.org/projects/1128)
-[![image](https://sourceforge.net/p/ruamel-yaml/code/ci/default/tree/_doc/_static/license.svg?format=raw)](https://opensource.org/licenses/MIT)
-[![image](https://sourceforge.net/p/ruamel-yaml/code/ci/default/tree/_doc/_static/pypi.svg?format=raw)](https://pypi.org/project/ruamel.yaml/)
-[![image](https://sourceforge.net/p/oitnb/code/ci/default/tree/_doc/_static/oitnb.svg?format=raw)](https://pypi.org/project/oitnb/)
-[![image](http://www.mypy-lang.org/static/mypy_badge.svg)](http://mypy-lang.org/)
-
-# ChangeLog
-
-0.18.5 (2023-11-03):
-
-- there is some indication that dependent packages have been pinned to use specific (tested) and just install the latest even in Python versions that have end-of-life
-
-0.18.4 (2023-11-01):
-
-- YAML() instance has a `doc_infos` attribute which is a cumulative list of DocInfo instances (one for `load()`, one per document for `load_all()`). DocInfo instances contain version information (requested, directive) and tag directive information
-- fix issue that the YAML instance tags attribute was not reset between documents, resulting in mixing of tag directives of multiple documents. Now only provides tag directive information on latest document after loading. This means tags for dumping must be set **again** after a document is loaded with the same instance. (because of this tags will be removed in a favour of a different mechanism in the future)
-- fix issue with multiple document intermixing YAML 1.2 and YAML 1.1, the VersionedResolver now resets
-- fix issue with disappearing comment when next token was Tag (still can't have both a comment before a tag and after a tag, before node)
-
-0.18.3 (2023-10-29):
-
-- fix issue with spurious newline on first item after comment + nested block sequence
-- additional links in the metadata on PyPI (Reported, with pointers how to fix, by [Sorin](https://sourceforge.net/u/ssbarnea/profile/)).
-
-0.18.2 (2023-10-24):
-
-- calling the deprecated functions now raises an `AttributeError` with the, somewhat more informative, orginal warning message. Instead of calling `sys.exit(1)`
-
-0.18.1 (2023-10-24):
-
-- calling the deprecated functions now always displays the warning message. (reported by [Trend Lloyd](https://sourceforge.net/u/lathiat2/profile/))
-
-0.18.0 (2023-10-23):
-
-- the **functions** `scan`, `parse`, `compose`, `load`, `emit`, `serialize`, `dump` and their variants (`_all`, `safe_`, `round_trip_`, etc) have been deprecated (the same named **methods** on `YAML()` instances are, of course, still there.
-- `YAML(typ='unsafe')` now issues a `PendingDeprecationWarning`. This will become deprecated in the 0.18 series
-(probably before the end of 2023).
-You can use `YAML(typ='full')` to dump unregistered Python classes/functions.
-For loading you'll have to register your classes/functions
-if you want the old, unsafe, functionality. You can still load any tag, like `!!python/name:posix.system', **safely**
-with the (default) round-trip parser.
-- fix for `bytes-like object is required not 'str' while dumping binary streams`. This was reported, analysed and a fix provided by [Vit Zikmund](https://sourceforge.net/u/tlwhitec/profile/)
-
-0.17.40 (2023-10-20):
-
-- flow style sets are now preserved ( `!!set {a, b, c} )`. Any values specified when loading are dropped, including `!!null ""`.
-- potential workaround for issue 484: the long_description_content_type including the variant specification `CommonMark`
-can result in problems on Azure. If you can install from `.tar.gz` using
-`RUAMEL_NO_LONG_DESCRIPTION=1 pip install ruamel.yaml --no-binary :all:` then the long description, and its
-offending type, are nog included (in the METADATA).
-(Reported by [Coury Ditch](https://sourceforge.net/u/cmditch/profile/))
-- links in documentation update (reported by [David Hoese](https://sourceforge.net/u/daveydave400/profile/))
-- Added some `__repr__` for internally used classes
-
-0.17.39 (2023-10-19):
-
-- update README generation, no code changes
-
-0.17.36 (2023-10-19):
-
-- fixed issue 480, dumping of a loaded empty flow-style mapping with comment failed (Reported by [Stéphane Brunner](https://sourceforge.net/u/stbrunner/profile/))
-- fixed issue 482, caused by DEFAULT_MAPPING_TAG having changes to being a `Tag()` instance, not a string (reported by [yan12125](https://sourceforge.net/u/yan12125/profile/))
-- updated documentation to use mkdocs
-
-0.17.35 (2023-10-04):
-
-- support for loading dataclasses with `InitVar` variables (some special coding was necessary to get the, unexecpected, default value in the corresponding instance attribute ( example of usage in [this question](https://stackoverflow.com/q/77228378/1307905))
-
-0.17.34 (2023-10-03):
-
-- Python 3.12 also loads C version when using `typ='safe'`
-- initial support for loading invoking
-`__post_init__()` on dataclasses that have that
-method after loading a registered dataclass.
-(Originally
-[asked](https://stackoverflow.com/q/51529458/1307905) on
-Stackoverflow by
-[nyanpasu64](https://stackoverflow.com/users/2683842/nyanpasu64)
-and as
-[ticket](https://sourceforge.net/p/ruamel-yaml/tickets/355/) by
-[Patrick Lehmann](https://sourceforge.net/u/paebbels/profile/))
-
-```
-@yaml.register_class
-@dataclass
-class ...
-```
-
-0.17.33 (2023-09-28):
-
-- added `flow_seq_start`, `flow_seq_end`, `flow_seq_separator`, `flow_map_start`, `flow_map_end`, `flow_map_separator` **class** attributes to the `Emitter` class so flow style output can more easily be influenced (based on [this answer](https://stackoverflow.com/a/76547814/1307905) on a StackOverflow question by [Huw Walters](https://stackoverflow.com/users/291033/huw-walters)).
-
-0.17.32 (2023-06-17):
-
-- fix issue with scanner getting stuck in infinite loop
-
-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
-
-0.17.25 (2023-05-09):
-
-- fix for regression while dumping wrapped strings with too many backslashes removed (issue 459, reported by [Lele Gaifax](https://sourceforge.net/u/lele/profile/))
-
-0.17.24 (2023-05-06):
-
-- rewrite of `CommentedMap.insert()`. If you have a merge key in the YAML document for the mapping you insert to, the position value should be the one as you look at the YAML input. This fixes issue 453 where other keys of a merged in mapping would show up after an insert (reported by [Alex Miller](https://sourceforge.net/u/millerdevel/profile/)). It also fixes a call to `.insert()` resulting into the merge key to move to be the first key if it wasn't already and it is also now possible to insert a key before a merge key (even if the fist key in the mapping).
-- fix (in the pure Python implementation including default) for issue 447. (reported by [Jack Cherng](https://sourceforge.net/u/jfcherng/profile/), also brought up by brent on [StackOverflow](https://stackoverflow.com/q/40072485/1307905))
-
-0.17.23 (2023-05-05):
-
-- fix 458, error on plain scalars starting with word longer than width. (reported by [Kyle Larose](https://sourceforge.net/u/klarose/profile/))
-- fix for `.update()` no longer correctly handling keyword arguments (reported by John Lin on [StackOverflow]( https://stackoverflow.com/q/76089100/1307905))
-- fix issue 454: high Unicode (emojis) in quoted strings always
-escaped (reported by [Michal
-Čihař](https://sourceforge.net/u/nijel/profile/) based on a
-question on StackOverflow).
-- fix issue with emitter conservatively inserting extra backslashes in wrapped quoted strings (reported by thebenman on [StackOverflow](https://stackoverflow.com/q/75631454/1307905))
-
-0.17.22 (2023-05-02):
-
-- fix issue 449 where the second exclamation marks got URL encoded (reported and fixing PR provided by [John Stark](https://sourceforge.net/u/jods/profile/))
-- fix issue with indent != 2 and literal scalars with empty first line (reported by wrdis on [StackOverflow](https://stackoverflow.com/q/75584262/1307905))
-- updated `__repr__` of CommentedMap, now that Python's dict is ordered -> no more `ordereddict(list-of-tuples)`
-- merge MR 4, handling OctalInt in YAML 1.1 (provided by [Jacob Floyd](https://sourceforge.net/u/cognifloyd/profile/))
-- fix loading of `!!float 42` (reported by Eric on [Stack overflow](https://stackoverflow.com/a/71555107/1307905))
-- line numbers are now set on `CommentedKeySeq` and `CommentedKeyMap` (which are created if you have a sequence resp. mapping as the key in a mapping)
-- plain scalars: put single words longer than width on a line of
-their own, instead of after the previous line (issue 427, reported
-by [Antoine
-Cotten](https://sourceforge.net/u/antoineco/profile/)). Caveat:
-this currently results in a space ending the previous line.
-- fix for folded scalar part of 421: comments after ">" on first
-line of folded scalars are now preserved (as were those in the
-same position on literal scalars). Issue reported by Jacob Floyd.
-- added stacklevel to warnings
-- typing changed from Py2 compatible comments to Py3, removed various Py2-isms
-
-0.17.21 (2022-02-12):
-
-- fix bug in calling `.compose()` method with `pathlib.Path` instance.
-
-0.17.20 (2022-01-03):
-
-- fix error in microseconds while rounding datetime fractions >= 9999995 (reported by [Luis Ferreira](https://sourceforge.net/u/ljmf00/))
-
-0.17.19 (2021-12-26):
-
-- fix mypy problems (reported by [Arun](https://sourceforge.net/u/arunppsg/profile/))
-
-0.17.18 (2021-12-24):
-
-- copy-paste error in folded scalar comment attachment (reported by [Stephan Geulette](https://sourceforge.net/u/sgeulette/profile/))
-- fix 411, indent error comment between key empty seq value (reported by [Guillermo Julián](https://sourceforge.net/u/gjulianm/profile/))
-
-0.17.17 (2021-10-31):
-
-- extract timestamp matching/creation to util
-
-0.17.16 (2021-08-28):
-
-- 398 also handle issue 397 when comment is newline
-
-0.17.15 (2021-08-28):
-
-- fix issue 397, insert comment before key when a comment between key and value exists (reported by [Bastien gerard](https://sourceforge.net/u/bagerard/))
-
-0.17.14 (2021-08-25):
-
-- fix issue 396, inserting key/val in merged-in dictionary (reported by [Bastien gerard](https://sourceforge.net/u/bagerard/))
-
-0.17.13 (2021-08-21):
-
-- minor fix in attr handling
-
-0.17.12 (2021-08-21):
-
-- fix issue with anchor on registered class not preserved and those classes using package attrs with `@attr.s()` (both reported by [ssph](https://sourceforge.net/u/sph/))
-
-0.17.11 (2021-08-19):
-
-- 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/))
-
-0.17.10 (2021-06-24):
-
-- fix issue 388, token with old comment structure != two elements (reported by [Dimitrios Bariamis](https://sourceforge.net/u/dbdbc/))
-
-0.17.9 (2021-06-10):
-
-- fix issue with updating CommentedMap (reported by sri on [StackOverflow](https://stackoverflow.com/q/67911659/1307905))
-
-0.17.8 (2021-06-09):
-
-- fix for issue 387 where templated anchors on tagged object did get set resulting in potential id reuse. (reported by [Artem Ploujnikov](https://sourceforge.net/u/flexthink/))
-
-0.17.7 (2021-05-31):
-
-- issue 385 also affected other deprecated loaders (reported via email by Oren Watson)
-
-0.17.6 (2021-05-31):
-
-- merged type annotations update provided by [Jochen Sprickerhof](https://sourceforge.net/u/jspricke/)
-- fix for issue 385: deprecated round_trip_loader function not
-working (reported by [Mike
-Gouline](https://sourceforge.net/u/gouline/))
-- wasted a few hours getting rid of mypy warnings/errors
-
-0.17.5 (2021-05-30):
-
-- fix for issue 384 `!!set` with aliased entry resulting in broken YAML on rt reported by [William Kimball](https://sourceforge.net/u/william303/))
-
-0.17.4 (2021-04-07):
-
-- prevent (empty) comments from throwing assertion error (issue 351 reported by [William Kimball](https://sourceforge.net/u/william303/)) comments (or empty line) will be dropped
-
-0.17.3 (2021-04-07):
-
-- fix for issue 382 caused by an error in a format string (reported by [William Kimball](https://sourceforge.net/u/william303/))
-- allow expansion of aliases by setting `yaml.composer.return_alias = lambda s: copy.deepcopy(s)`
-(as per [Stackoverflow answer](https://stackoverflow.com/a/66983530/1307905))
-
-0.17.2 (2021-03-29):
-
-- change -py2.py3-none-any.whl to -py3-none-any.whl, and remove 0.17.1
-
-0.17.1 (2021-03-29):
-
-- added 'Programming Language :: Python :: 3 :: Only', and
-removing 0.17.0 from PyPI (reported by [Alasdair
-Nicol](https://sourceforge.net/u/alasdairnicol/))
-
-0.17.0 (2021-03-26):
-
-- removed because of incomplete classifiers
-- this release no longer supports Python 2.7, most if not all Python 2 specific code is removed. The 0.17.x series is the last to support Python 3.5 (this also allowed for removal of the dependency on `ruamel.std.pathlib`)
-- remove Python2 specific code branches and adaptations (u-strings)
-- prepare % code for f-strings using `_F`
-- allow PyOxidisation ([issue 324](https://sourceforge.net/p/ruamel-yaml/tickets/324/) resp. [issue 171](https://github.com/indygreg/PyOxidizer/issues/171))
-- replaced Python 2 compatible enforcement of keyword arguments with '*'
-- the old top level *functions* `load`, `safe_load`, `round_trip_load`, `dump`, `safe_dump`, `round_trip_dump`, `scan`, `parse`, `compose`, `emit`, `serialize` as well as their `_all` variants for multi-document streams, now issue a `PendingDeprecationning` (e.g. when run from pytest, but also Python is started with `-Wd`). Use the methods on `YAML()`, which have been extended.
-- fix for issue 376: indentation changes could put literal/folded
-scalar to start before the `#` column of a following comment.
-Effectively making the comment part of the scalar in the output.
-(reported by [Bence Nagy](https://sourceforge.net/u/underyx/))
-
-------------------------------------------------------------------------
-
-For older changes see the file
-[CHANGES](https://sourceforge.net/p/ruamel-yaml/code/ci/default/tree/CHANGES)
diff --git a/ruamel.yaml.egg-info/SOURCES.txt b/ruamel.yaml.egg-info/SOURCES.txt
deleted file mode 100644
index 1b1dbe7..0000000
--- a/ruamel.yaml.egg-info/SOURCES.txt
+++ /dev/null
@@ -1,44 +0,0 @@
-CHANGES
-LICENSE
-MANIFEST.in
-README.md
-pyproject.toml
-setup.py
-./LICENSE
-./__init__.py
-./anchor.py
-./comments.py
-./compat.py
-./composer.py
-./configobjwalker.py
-./constructor.py
-./cyaml.py
-./docinfo.py
-./dumper.py
-./emitter.py
-./error.py
-./events.py
-./loader.py
-./main.py
-./nodes.py
-./parser.py
-./py.typed
-./reader.py
-./representer.py
-./resolver.py
-./scalarbool.py
-./scalarfloat.py
-./scalarint.py
-./scalarstring.py
-./scanner.py
-./serializer.py
-./tag.py
-./timestamp.py
-./tokens.py
-./util.py
-ruamel.yaml.egg-info/PKG-INFO
-ruamel.yaml.egg-info/SOURCES.txt
-ruamel.yaml.egg-info/dependency_links.txt
-ruamel.yaml.egg-info/not-zip-safe
-ruamel.yaml.egg-info/requires.txt
-ruamel.yaml.egg-info/top_level.txt \ No newline at end of file
diff --git a/ruamel.yaml.egg-info/dependency_links.txt b/ruamel.yaml.egg-info/dependency_links.txt
deleted file mode 100644
index 8b13789..0000000
--- a/ruamel.yaml.egg-info/dependency_links.txt
+++ /dev/null
@@ -1 +0,0 @@
-
diff --git a/ruamel.yaml.egg-info/not-zip-safe b/ruamel.yaml.egg-info/not-zip-safe
deleted file mode 100644
index 8b13789..0000000
--- a/ruamel.yaml.egg-info/not-zip-safe
+++ /dev/null
@@ -1 +0,0 @@
-
diff --git a/ruamel.yaml.egg-info/requires.txt b/ruamel.yaml.egg-info/requires.txt
deleted file mode 100644
index c8c22f0..0000000
--- a/ruamel.yaml.egg-info/requires.txt
+++ /dev/null
@@ -1,10 +0,0 @@
-
-[:platform_python_implementation=="CPython" and python_version<"3.13"]
-ruamel.yaml.clib>=0.2.7
-
-[docs]
-ryd
-mercurial>5.7
-
-[jinja2]
-ruamel.yaml.jinja2>=0.2
diff --git a/ruamel.yaml.egg-info/top_level.txt b/ruamel.yaml.egg-info/top_level.txt
deleted file mode 100644
index 282b116..0000000
--- a/ruamel.yaml.egg-info/top_level.txt
+++ /dev/null
@@ -1 +0,0 @@
-ruamel
diff --git a/scalarbool.py b/scalarbool.py
index 083d3cb..c38dd6b 100644
--- a/scalarbool.py
+++ b/scalarbool.py
@@ -1,4 +1,5 @@
-# coding: utf-8
+
+from __future__ import annotations
"""
You cannot subclass bool, and this is necessary for round-tripping anchored
@@ -11,7 +12,8 @@ You can use these in an if statement, but not when testing equivalence
from ruamel.yaml.anchor import Anchor
-from typing import Text, Any, Dict, List # NOQA
+if False: # MYPY
+ from typing import Text, Any, Dict, List # NOQA
__all__ = ['ScalarBoolean']
diff --git a/scalarfloat.py b/scalarfloat.py
index 10b4c29..997cabc 100644
--- a/scalarfloat.py
+++ b/scalarfloat.py
@@ -1,9 +1,11 @@
-# coding: utf-8
+
+from __future__ import annotations
import sys
from ruamel.yaml.anchor import Anchor
-from typing import Text, Any, Dict, List # NOQA
+if False: # MYPY
+ from typing import Text, Any, Dict, List # NOQA
__all__ = ['ScalarFloat', 'ExponentialFloat', 'ExponentialCapsFloat']
diff --git a/scalarint.py b/scalarint.py
index af798b7..c9343d7 100644
--- a/scalarint.py
+++ b/scalarint.py
@@ -1,8 +1,10 @@
-# coding: utf-8
+
+from __future__ import annotations
from ruamel.yaml.anchor import Anchor
-from typing import Text, Any, Dict, List # NOQA
+if False: # MYPY
+ from typing import Text, Any, Dict, List # NOQA
__all__ = ['ScalarInt', 'BinaryInt', 'OctalInt', 'HexInt', 'HexCapsInt', 'DecimalInt']
diff --git a/scalarstring.py b/scalarstring.py
index 30f4fde..32d9f1c 100644
--- a/scalarstring.py
+++ b/scalarstring.py
@@ -1,9 +1,11 @@
-# coding: utf-8
+
+from __future__ import annotations
from ruamel.yaml.anchor import Anchor
-from typing import Text, Any, Dict, List # NOQA
-from ruamel.yaml.compat import SupportsIndex
+if False: # MYPY
+ from typing import Text, Any, Dict, List # NOQA
+ from ruamel.yaml.compat import SupportsIndex
__all__ = [
'ScalarString',
diff --git a/scanner.py b/scanner.py
index 65d9a77..650d544 100644
--- a/scanner.py
+++ b/scanner.py
@@ -1,4 +1,5 @@
-# coding: utf-8
+
+from __future__ import annotations
# Scanner produces tokens of the following types:
# STREAM-START
@@ -28,13 +29,13 @@
# Read comments in the Scanner code for more details.
#
-import inspect
from ruamel.yaml.error import MarkedYAMLError, CommentMark # NOQA
from ruamel.yaml.tokens import * # NOQA
from ruamel.yaml.docinfo import Version, Tag # NOQA
-from ruamel.yaml.compat import check_anchorname_char, nprint, nprintf # NOQA
+from ruamel.yaml.compat import check_anchorname_char, _debug, nprint, nprintf # NOQA
-from typing import Any, Dict, Optional, List, Union, Text, Tuple # NOQA
+if False: # MYPY
+ from typing import Any, Dict, Optional, List, Union, Text, Tuple # NOQA
__all__ = ['Scanner', 'RoundTripScanner', 'ScannerError']
@@ -44,9 +45,9 @@ _THE_END_SPACE_TAB = ' \n\0\t\r\x85\u2028\u2029'
_SPACE_TAB = ' \t'
-def xprintf(*args: Any, **kw: Any) -> Any:
- return nprintf(*args, **kw)
- pass
+if _debug != 0:
+ def xprintf(*args: Any, **kw: Any) -> Any:
+ return nprintf(*args, **kw)
class ScannerError(MarkedYAMLError):
@@ -1983,17 +1984,23 @@ class CommentBase:
self.line = line
self.column = column
self.used = ' '
- info = inspect.getframeinfo(inspect.stack()[3][0])
- self.function = info.function
- self.fline = info.lineno
- self.ufun = None
- self.uline = None
+ if _debug != 0:
+ import inspect
+
+ info = inspect.getframeinfo(inspect.stack()[3][0])
+ self.function = info.function
+ self.fline = info.lineno
+ self.ufun = None
+ self.uline = None
def set_used(self, v: Any = '+') -> None:
self.used = v
- info = inspect.getframeinfo(inspect.stack()[1][0])
- self.ufun = info.function # type: ignore
- self.uline = info.lineno # type: ignore
+ if _debug != 0:
+ import inspect
+
+ info = inspect.getframeinfo(inspect.stack()[1][0])
+ self.ufun = info.function # type: ignore
+ self.uline = info.lineno # type: ignore
def set_assigned(self) -> None:
self.used = '|'
@@ -2091,22 +2098,29 @@ class ScannedComments:
def unprocessed(self, use: Any = False) -> Any:
while len(self.unused) > 0:
- first = self.unused.pop(0) if use else self.unused[0]
- info = inspect.getframeinfo(inspect.stack()[1][0])
- xprintf('using', first, self.comments[first].value, info.function, info.lineno)
+ if _debug != 0:
+ import inspect
+
+ first = self.unused.pop(0) if use else self.unused[0]
+ info = inspect.getframeinfo(inspect.stack()[1][0])
+ xprintf('using', first, self.comments[first].value, info.function, info.lineno)
yield first, self.comments[first]
if use:
self.comments[first].set_used()
def assign_pre(self, token: Any) -> Any:
token_line = token.start_mark.line
- info = inspect.getframeinfo(inspect.stack()[1][0])
- xprintf('assign_pre', token_line, self.unused, info.function, info.lineno)
+ if _debug != 0:
+ import inspect
+
+ info = inspect.getframeinfo(inspect.stack()[1][0])
+ xprintf('assign_pre', token_line, self.unused, info.function, info.lineno)
gobbled = False
while self.unused and self.unused[0] < token_line:
gobbled = True
first = self.unused.pop(0)
- xprintf('assign_pre < ', first)
+ if _debug != 0:
+ xprintf('assign_pre < ', first)
self.comments[first].set_used()
token.add_comment_pre(first)
return gobbled
@@ -2123,7 +2137,8 @@ class ScannedComments:
tokens[-idx], ValueToken,
):
idx += 1
- xprintf('idx1', idx)
+ if _debug != 0:
+ xprintf('idx1', idx)
if (
len(tokens) > idx
and isinstance(tokens[-idx], ScalarToken)
@@ -2137,13 +2152,15 @@ class ScannedComments:
try:
eol_idx = self.unused.pop(0)
self.comments[eol_idx].set_used()
- xprintf('>>>>>a', idx, eol_idx, KEYCMNT)
+ if _debug != 0:
+ xprintf('>>>>>a', idx, eol_idx, KEYCMNT)
tokens[-idx].add_comment_eol(eol_idx, KEYCMNT)
except IndexError:
raise NotImplementedError
return
except IndexError:
- xprintf('IndexError1')
+ if _debug != 0:
+ xprintf('IndexError1')
pass
try:
if isinstance(tokens[-idx], ScalarToken) and isinstance(
@@ -2157,24 +2174,30 @@ class ScannedComments:
raise NotImplementedError
return
except IndexError:
- xprintf('IndexError2')
+ if _debug != 0:
+ xprintf('IndexError2')
pass
for t in tokens:
xprintf('tt-', t)
- xprintf('not implemented EOL', type(tokens[-idx]))
+ if _debug != 0:
+ xprintf('not implemented EOL', type(tokens[-idx]))
import sys
sys.exit(0)
def assign_post(self, token: Any) -> Any:
token_line = token.start_mark.line
- info = inspect.getframeinfo(inspect.stack()[1][0])
- xprintf('assign_post', token_line, self.unused, info.function, info.lineno)
+ if _debug != 0:
+ import inspect
+
+ info = inspect.getframeinfo(inspect.stack()[1][0])
+ xprintf('assign_post', token_line, self.unused, info.function, info.lineno)
gobbled = False
while self.unused and self.unused[0] < token_line:
gobbled = True
first = self.unused.pop(0)
- xprintf('assign_post < ', first)
+ if _debug != 0:
+ xprintf('assign_post < ', first)
self.comments[first].set_used()
token.add_comment_post(first)
return gobbled
@@ -2222,11 +2245,12 @@ class RoundTripScannerSC(Scanner): # RoundTripScanner Split Comments
if self.tokens[0].start_mark.line == self.tokens[-1].start_mark.line:
return True
if True:
- xprintf('-x--', len(self.tokens))
- for t in self.tokens:
- xprintf(t)
- # xprintf(self.comments.last())
- xprintf(self.comments.str_unprocessed()) # type: ignore
+ if _debug != 0:
+ xprintf('-x--', len(self.tokens))
+ for t in self.tokens:
+ xprintf(t)
+ # xprintf(self.comments.last())
+ xprintf(self.comments.str_unprocessed()) # type: ignore
self.comments.assign_pre(self.tokens[0]) # type: ignore
self.comments.assign_eol(self.tokens) # type: ignore
return False
diff --git a/serializer.py b/serializer.py
index 1ac46d2..90f7c11 100644
--- a/serializer.py
+++ b/serializer.py
@@ -1,4 +1,5 @@
-# coding: utf-8
+
+from __future__ import annotations
from ruamel.yaml.error import YAMLError
from ruamel.yaml.compat import nprint, DBG_NODE, dbg, nprintf # NOQA
@@ -18,8 +19,9 @@ from ruamel.yaml.events import (
)
from ruamel.yaml.nodes import MappingNode, ScalarNode, SequenceNode
-from typing import Any, Dict, Union, Text, Optional # NOQA
-from ruamel.yaml.compat import VersionType # NOQA
+if False: # MYPY
+ from typing import Any, Dict, Union, Text, Optional # NOQA
+ from ruamel.yaml.compat import VersionType # NOQA
__all__ = ['Serializer', 'SerializerError']
diff --git a/setup.cfg b/setup.cfg
deleted file mode 100644
index 8bfd5a1..0000000
--- a/setup.cfg
+++ /dev/null
@@ -1,4 +0,0 @@
-[egg_info]
-tag_build =
-tag_date = 0
-
diff --git a/tag.py b/tag.py
index 7ad23fe..9a4cad9 100644
--- a/tag.py
+++ b/tag.py
@@ -1,4 +1,5 @@
-# coding: utf-8
+
+from __future__ import annotations
"""
In round-trip mode the original tag needs to be preserved, but the tag
@@ -10,7 +11,8 @@ A Tag that is created for dumping only (on an object loaded without a tag) has a
only.
"""
-from typing import Any, Dict, Optional, List, Union, Optional, Iterator # NOQA
+if False: # MYPY
+ from typing import Any, Dict, Optional, List, Union, Optional, Iterator # NOQA
tag_attrib = '_yaml_tag'
diff --git a/timestamp.py b/timestamp.py
index 753dfc1..a9aad05 100644
--- a/timestamp.py
+++ b/timestamp.py
@@ -1,19 +1,21 @@
-# coding: utf-8
-import datetime
+from __future__ import annotations
+
import copy
+import datetime
# 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
+if False: # MYPY
+ from typing import Any, Dict, Optional, List # NOQA
class TimeStamp(datetime.datetime):
def __init__(self, *args: Any, **kw: Any) -> None:
- self._yaml: Dict[Any, Any] = dict(t=False, tz=None, delta=0)
+ self._yaml: Dict[str, Any] = dict(t=False, tz=None, delta=0)
def __new__(cls, *args: Any, **kw: Any) -> Any: # datetime is immutable
return datetime.datetime.__new__(cls, *args, **kw)
diff --git a/tokens.py b/tokens.py
index 0c73dcf..700bd03 100644
--- a/tokens.py
+++ b/tokens.py
@@ -1,8 +1,10 @@
-# coding: utf-8
+
+from __future__ import annotations
from ruamel.yaml.compat import nprintf # NOQA
-from typing import Text, Any, Dict, Optional, List # NOQA
+if False: # MYPY
+ from typing import Text, Any, Dict, Optional, List # NOQA
from .error import StreamMark # NOQA
SHOW_LINES = True
diff --git a/tox.ini b/tox.ini
new file mode 100755
index 0000000..0c4a722
--- /dev/null
+++ b/tox.ini
@@ -0,0 +1,49 @@
+[tox]
+# toxworkdir = /data1/DATA/tox/ruamel.yaml
+envlist = cs,py311,py310,py39,py38,py37,py312
+
+[testenv]
+allowlist_externals = /bin/bash
+install_command = pip install --disable-pip-version-check {opts} {packages}
+commands =
+ /bin/bash -c 'pytest {posargs} _test/test_*.py'
+deps =
+ pytest
+ setuptools
+
+[testenv:cs]
+basepython = python3.11
+deps =
+ flake8
+ flake8-bugbear;python_version>="3.11"
+ flake8-2020==1.8.1
+ flake8-commas==2.1.0
+ flake8-comprehensions==3.14.0
+ flake8-length==0.3.1
+ flake8-logging-format==0.9.0
+commands =
+ flake8 []{posargs}
+
+[testenv:pep8]
+basepython = python3.11
+deps =
+ flake8
+ flake8-bugbear;python_version>="3.11"
+ flake8-2020==1.8.1
+ flake8-commas==2.1.0
+ flake8-comprehensions==3.14.0
+ flake8-length==0.3.1
+ flake8-logging-format==0.9.0
+commands =
+ flake8 []{posargs}
+
+[flake8]
+show-source = True
+max-line-length = 95
+ignore = W503,F405,E203,C408
+exclude = _test/lib,branch_default,.hg,.git,.tox,dist,.cache,__pycache__,ruamel.zip2tar.egg-info
+
+[pytest]
+filterwarnings =
+ error::DeprecationWarning
+ error::PendingDeprecationWarning
diff --git a/util.py b/util.py
index b621ce0..17cb2d6 100644
--- a/util.py
+++ b/util.py
@@ -1,4 +1,5 @@
-# coding: utf-8
+
+from __future__ import annotations
"""
some helper functions that might be generally useful
@@ -9,8 +10,9 @@ from functools import partial
import re
-from typing import Any, Dict, Optional, List, Text, Callable, Union # NOQA
-from .compat import StreamTextType # NOQA
+if False: # MYPY
+ from typing import Any, Dict, Optional, List, Text, Callable, Union # NOQA
+ from .compat import StreamTextType # NOQA
class LazyEval:
@@ -73,12 +75,12 @@ def create_timestamp(
tz_hour: Any,
tz_minute: Any,
) -> Union[datetime.datetime, datetime.date]:
- # create a timestamp from match against timestamp_regexp
+ # create a timestamp from matching against timestamp_regexp
MAX_FRAC = 999999
year = int(year)
month = int(month)
day = int(day)
- if not hour:
+ if hour is None:
return datetime.date(year, month, day)
hour = int(hour)
minute = int(minute)
@@ -97,16 +99,20 @@ def create_timestamp(
fraction = frac
else:
fraction = 0
+ tzinfo = None
delta = None
if tz_sign:
tz_hour = int(tz_hour)
tz_minute = int(tz_minute) if tz_minute else 0
- delta = datetime.timedelta(
- hours=tz_hour, minutes=tz_minute, seconds=1 if frac > MAX_FRAC else 0,
+ td = datetime.timedelta(
+ hours=tz_hour, minutes=tz_minute,
)
if tz_sign == '-':
- delta = -delta
- elif frac > MAX_FRAC:
+ td = -td
+ tzinfo = datetime.timezone(td, name=tz)
+ elif tz == 'Z':
+ tzinfo = datetime.timezone(datetime.timedelta(hours=0), name=tz)
+ if frac > MAX_FRAC:
delta = -datetime.timedelta(seconds=1)
# should do something else instead (or hook this up to the preceding if statement
# in reverse
@@ -116,7 +122,7 @@ def create_timestamp(
# datetime.timezone.utc)
# the above is not good enough though, should provide tzinfo. In Python3 that is easily
# doable drop that kind of support for Python2 as it has not native tzinfo
- data = datetime.datetime(year, month, day, hour, minute, second, fraction)
+ data = datetime.datetime(year, month, day, hour, minute, second, fraction, tzinfo)
if delta:
data -= delta
return data