summaryrefslogtreecommitdiffstats
path: root/_doc
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 /_doc
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>
Diffstat (limited to '')
-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
15 files changed, 2220 insertions, 0 deletions
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