summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--CHANGES18
-rw-r--r--README.rst6
-rw-r--r--__init__.py4
-rw-r--r--_doc/api.rst9
-rw-r--r--_doc/basicuse.rst20
-rw-r--r--_doc/detail.rst44
-rw-r--r--_doc/example.rst151
-rw-r--r--_doc/install.rst7
-rw-r--r--_doc/overview.rst31
-rw-r--r--_doc/pyyaml.rst9
-rw-r--r--_example/add_comment.py (renamed from example/add_comment.py)7
-rw-r--r--_example/anchor_merge.py (renamed from example/anchor_merge.py)5
-rw-r--r--_example/map_insert.py13
-rw-r--r--_example/small.py16
-rw-r--r--_example/small_o.py21
-rw-r--r--_example/so_10241882.py (renamed from example/so_10241882.py)0
-rw-r--r--_example/so_13517753.py (renamed from example/so_13517753.py)0
-rw-r--r--_example/transform.py39
-rw-r--r--_test/test_api_change.py63
-rw-r--r--example/small.py16
-rw-r--r--main.py26
-rw-r--r--tox.ini1
22 files changed, 409 insertions, 97 deletions
diff --git a/CHANGES b/CHANGES
index 57899f7..791b687 100644
--- a/CHANGES
+++ b/CHANGES
@@ -1,12 +1,14 @@
+[0, 15, 4]: 2017-06-08
+ - `transform` parameter on dump that expects a function taking a
+ string and returning a string. This allows transformation of the output
+ before it is written to stream.
+ - some updates to the docs
+
[0, 15, 3]: 2017-06-07
- No longer try to compile C extensions on Windows. Compilation can be forced by setting
the environment variable `RUAMEL_FORCE_EXT_BUILD` to some value
before starting the `pip install`.
-[0, 15, 3]: 2017-06-07
- - No longer try to compile C extensions on Windows. Compilation can be forced by setting
- the environment variable `RUAMEL_FORCE_EXT_BUILD` before starting the `pip install`.
-
[0, 15, 2]: 2017-06-07
- update to conform to mypy 0.511: mypy --strict
@@ -23,14 +25,6 @@
- assigning a normal string value to an existing CommentedMap key or CommentedSeq
element will result in a value cast to the previous value's type if possible.
-[0, 15, 0]: 2017-06-04
- - it is no allowed to pass in a ``pathlib.Path`` as "stream" parameter to all
- load/dump functions
- - passing in a non-supported object (e.g. a string) as "stream" will result in a
- much more meaningful YAMLStreamError.
- - assigning a normal string value to an existing CommentedMap key or CommentedSeq
- element will result in a value cast to the previous value's type if possible.
-
[0, 14, 12]: 2017-05-14
- fix for issue 119, deepcopy not returning subclasses (reported and PR by
Constantine Evans <cevans@evanslabs.org>)
diff --git a/README.rst b/README.rst
index 0041f72..a4a7f38 100644
--- a/README.rst
+++ b/README.rst
@@ -32,6 +32,12 @@ ChangeLog
.. should insert NEXT: at the beginning of line for next key
+0.15.4 (2017-06-08):
+ - `transform` parameter on dump that expects a function taking a
+ string and returning a string. This allows transformation of the output
+ before it is written to stream. This forces creation of the complete output in memory!
+ - some updates to the docs
+
0.15.3 (2017-06-07):
- No longer try to compile C extensions on Windows. Compilation can be forced by setting
the environment variable `RUAMEL_FORCE_EXT_BUILD` to some value
diff --git a/__init__.py b/__init__.py
index be51feb..889feba 100644
--- a/__init__.py
+++ b/__init__.py
@@ -11,8 +11,8 @@ if False: # MYPY
_package_data = dict(
full_package_name='ruamel.yaml',
- version_info=(0, 15, 3),
- __version__='0.15.3',
+ version_info=(0, 15, 4),
+ __version__='0.15.4',
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/api.rst b/_doc/api.rst
index 512d726..cf963ca 100644
--- a/_doc/api.rst
+++ b/_doc/api.rst
@@ -12,6 +12,7 @@ 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,
@@ -185,7 +186,7 @@ created that were discarded on termination of the function.
This way of doing things leads to several problems:
-- it is impossible to return information to the caller apart from the
+- 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
@@ -195,7 +196,11 @@ This way of doing things leads to several problems:
- 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
+ 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
diff --git a/_doc/basicuse.rst b/_doc/basicuse.rst
index 935e83a..05b59fc 100644
--- a/_doc/basicuse.rst
+++ b/_doc/basicuse.rst
@@ -18,7 +18,9 @@ You load a YAML document using::
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 unknow tags.
+document without resolving unknow tags. Provide `pure=True` to
+enforce using the pure Python implementation (faster C libraries will be used
+when possible/available)
Dumping works in the same way::
@@ -29,13 +31,17 @@ Dumping works in the same way::
yaml.dump({a: [1, 2], s)
in this ``s`` can be a file pointer (i.e. an object that has the
-`.write()` method, a ``pathlib.Path()`` or ``None`` (the default, which causes the
-YAML documented to be returned as a string.
+`.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:
+
+ def tr(s):
+ return s.replace('\n', '<\n') # such output is not valid YAML!
+
+ yaml.dump(data, sys.stdout, transform=tr)
-*If you have `yaml.dump()`
-return the YAML doc as string, do not just ``print`` that returned
-value*. In that case use `yaml.dump(data, sys.stdout)`, which is more
-efficient (and shows that you know what you are doing).
More examples
-------------
diff --git a/_doc/detail.rst b/_doc/detail.rst
index 272b70c..1a2d68e 100644
--- a/_doc/detail.rst
+++ b/_doc/detail.rst
@@ -26,6 +26,12 @@ Details
(``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
------------------------------
@@ -43,7 +49,7 @@ back to::
- b: 1
- 2
-if you specify ``indent=4``.
+if you specify ``yaml.indent = 4``.
PyYAML (and older versions of ruamel.yaml) gives you non-indented
scalars (when specifying default_flow_style=False)::
@@ -52,10 +58,10 @@ scalars (when specifying default_flow_style=False)::
- b: 1
- 2
-The dump routine also has an additional ``block_seq_indent`` parameter that
+The dump also observes an additional ``block_seq_indent`` settingr that
can be used to push the dash inwards, *within the space defined by* ``indent``.
-The above example with the often seen ``indent=4, block_seq_indent=2``
+The above example with the often seen ``yaml.indent = 4; yaml.block_seq_indent = 2``
indentation::
x:
@@ -69,14 +75,14 @@ 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
-``indent=3`` resp. ``block_seq_indent=2``, migth give correct, but counter
+``yaml.indent = 3`` resp. ``yaml.block_seq_indent = 2``, migth give correct, but counter
intuitive results.
**It is best to always have** ``indent >= block_seq_indent + 2``
**but this is not enforced**. Depending on your structure, not following
this advice **might lead to invalid output**.
-Positioning ':' in top level mappings, prefix in ':'
+Positioning ':' in top level mappings, prefixing ':'
----------------------------------------------------
If you want your toplevel mappings to look like::
@@ -85,12 +91,12 @@ If you want your toplevel mappings to look like::
comment : |
this is just a first try
-then call ``round_trip_dump()`` with ``top_level_colon_align=True``
-(and ``indent=4``). ``True`` causes calculation based on the longest key,
+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
-``prefix_colon=' '``::
+``yaml.prefix_colon = ' '``::
- https://myurl/abc.tar.xz : 23445
# ^ extra space here
@@ -98,7 +104,7 @@ If you want an extra space between a mapping key and the colon specify
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 ``top_level_colon_align=12`` where ``12`` has to be an
+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.
@@ -122,15 +128,8 @@ The 1.2 version does **not** support:
- 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:
-
- ruamel.yaml.load(some_str, Loader=ruamel.yaml.RoundTripLoader, version=(1, 1))
-
-or the equivalent (version can be a tuple, list or string):
-
- ruamel.yaml.round_trip_load(some_str, version="1.1")
-
-this also works for ``load_all``/``round_trip_load_all``.
+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.*
@@ -154,8 +153,11 @@ for for this is::
from __future__ import print_function
+ import sys
import ruamel.yaml
+ yaml = ruamel.yaml.YAML() # defaults to round-trip
+
inp = """\
abc:
- a # comment 1
@@ -168,14 +170,14 @@ for for this is::
f: 6 # comment 3
"""
- data = ruamel.yaml.load(inp, ruamel.yaml.RoundTripLoader)
+ 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', 'd', column=20)
- print(ruamel.yaml.dump(data, Dumper=ruamel.yaml.RoundTripDumper), end='')
+ yaml.dump(data, sys.stdout)
.. example code add_comment.py
@@ -263,6 +265,6 @@ included and you can do::
"""
- data = yaml.load(yaml_str, Loader=yaml.RoundTripLoader)
+ data = yaml.load(yaml_str)
assert data.mlget(['a', 1, 'd', 'f'], list_ok=True) == 196
diff --git a/_doc/example.rst b/_doc/example.rst
index 4e8ac4a..000b06e 100644
--- a/_doc/example.rst
+++ b/_doc/example.rst
@@ -4,8 +4,41 @@ Examples
Basic round trip of parsing YAML to Python objects, modifying
and generating YAML::
+ 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)
+
+.. example code small.py
+
+Resulting in ::
+
+ # example
+ name:
+ # details
+ family: Smith # very common
+ given: Bob # one of the siblings
+
+
+.. example output small.py
+
+with the old API::
+
from __future__ import print_function
+ import sys
import ruamel.yaml
inp = """\
@@ -19,9 +52,13 @@ and generating YAML::
code = ruamel.yaml.load(inp, ruamel.yaml.RoundTripLoader)
code['name']['given'] = 'Bob'
- print(ruamel.yaml.dump(code, Dumper=ruamel.yaml.RoundTripDumper), end='')
+ ruamel.yaml.dump(code, sys.stdout, Dumper=ruamel.yaml.RoundTripDumper)
-.. example code small.py
+ # the last statement can be done less efficient in time and memory with
+ # leaving out the end='' would cause a double newline at the end
+ # print(ruamel.yaml.dump(code, Dumper=ruamel.yaml.RoundTripDumper), end='')
+
+.. example code small_o.py
Resulting in ::
@@ -32,7 +69,8 @@ Resulting in ::
given: Bob # one of the siblings
-.. example output small.py
+.. example output small_o.py
+
----
@@ -40,7 +78,7 @@ YAML handcrafted anchors and references as well as key merging
are preserved. The merged keys can transparently be accessed
using ``[]`` and ``.get()``::
- import ruamel.yaml
+ from ruamel.yaml import YAML
inp = """\
- &CENTER {x: 1, y: 2}
@@ -66,26 +104,33 @@ using ``[]`` and ``.get()``::
label: center/big
"""
- data = ruamel.yaml.load(inp, ruamel.yaml.RoundTripLoader)
+ yaml = YAML()
+ data = yaml.load(inp)
assert data[7]['y'] == 2
-
.. example code anchor_merge.py
+
----
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::
+ 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...
"""
- data = ruamel.yaml.round_trip_load(yaml_str)
+ yaml = YAML()
+ data = yaml.load(yaml_str)
data.insert(1, 'last name', 'Vandelay', comment="new key")
- print(ruamel.yaml.round_trip_dump(data))
+ yaml.dump(data, sys.stdout)
+
+.. example code map_insert.py
gives::
@@ -94,9 +139,99 @@ gives::
occupation: Architect # This is an occupation comment
about: Art Vandelay is a fictional character that George invents...
+
+.. example output map_insert.py
+
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".
+
+The following program with three dumps::
+
+
+ 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 = 4
+ yaml.block_seq_indent = 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)
+
+.. example code transform.py
+
+gives as output::
+
+ ---
+ 1:
+ 1:
+ - 1: 1
+ 2: 2
+ - 1: 1
+ 2: 2
+ 2: 2
+ 2: 42
+ ---
+ 1:
+ 1:
+ - 1: 1
+ 2: 2
+ - 1: 1
+ 2: 2
+ 2: 2
+ 2: 42
+ ---
+ 1:
+ 1:
+ - 1: 1
+ 2: 2
+ - 1: 1
+ 2: 2
+ 2: 2
+ 2: 42
+
+
+.. example output transform.py
+
+
+The transform example was inspired by a `question
+<https://stackoverflow.com/q/44388701/1307905>`_ posted by *nowox* on
+StackOverflow.
diff --git a/_doc/install.rst b/_doc/install.rst
index be3c4f4..e90f9e7 100644
--- a/_doc/install.rst
+++ b/_doc/install.rst
@@ -1,11 +1,11 @@
Installing
==========
-``ruamel.yaml`` can be installed from PyPI_ using::
+``ruamel.yaml`` should be installed from PyPI_ using::
pip install ruamel.yaml
-There is a a commandline utility ``yaml`` available after installing::
+There also is a commandline utility ``yaml`` available after installing::
pip install ruamel.yaml.cmd
@@ -17,7 +17,7 @@ Optional requirements
If you have the C yaml library and headers installed, as well as
the header files for your Python executables then you can use the
-non-roundtrip but faster C loader and emitter.
+non-roundtrip, but faster, C loader and emitter.
On Debian systems you should use::
@@ -28,4 +28,3 @@ you can leave out ``python3-dev`` if you don't use python3
For CentOS (7) based systems you should do::
sudo yum install libyaml-devel python-devel
-
diff --git a/_doc/overview.rst b/_doc/overview.rst
index 1c664e0..18af771 100644
--- a/_doc/overview.rst
+++ b/_doc/overview.rst
@@ -3,17 +3,17 @@ 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>`_
+<https://bitbucket.org/xi/pyyaml>`_.
``ruamel.yaml`` supports `YAML 1.2`_ and has round-trip loaders and dumpers
that preserves, among others:
- comments
-- block style and key ordering are kept, so you can diff the round-tripped
+- 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)
-- anchors names that are hand-crafted (i.e. not of the form``idNNN``)
+- 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
@@ -22,10 +22,25 @@ Reassigning values or replacing list items, etc., is fine.
For the specific 1.2 differences see :ref:`yaml-1-2-support`
-Although individual indentation is not preserved, you can specify both
-indentation and specific offset of block sequence dashes within that
-indentation. There is a utility function that tries to determine the
-correct values for ``indent`` and ``block_seq_indent`` that can
-then be passed to the dumper.
+Although individual indentation of lines is not preserved, you can
+specify both indentation (counting for this does **not** include the
+dash for a sequence element) and specific offset of block sequence
+dashes within that indentation. There is a utility function that tries
+to determine the correct values for ``indent`` and
+``block_seq_indent`` that can then be passed to the dumper.
+
+
+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``
+
.. include:: links.rst
diff --git a/_doc/pyyaml.rst b/_doc/pyyaml.rst
index 93dcd65..822efd8 100644
--- a/_doc/pyyaml.rst
+++ b/_doc/pyyaml.rst
@@ -47,7 +47,7 @@ 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 (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 version specific.
+miniscule part of the code is Python version specific.
Fixes
-----
@@ -68,4 +68,11 @@ 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.
+
+
.. include:: links.rst
diff --git a/example/add_comment.py b/_example/add_comment.py
index f4809bb..b865b2a 100644
--- a/example/add_comment.py
+++ b/_example/add_comment.py
@@ -1,7 +1,10 @@
from __future__ import print_function
+import sys
import ruamel.yaml
+yaml = ruamel.yaml.YAML() # defaults to round-trip
+
inp = """\
abc:
- a # comment 1
@@ -14,11 +17,11 @@ xyz:
f: 6 # comment 3
"""
-data = ruamel.yaml.load(inp, ruamel.yaml.RoundTripLoader)
+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', 'd', column=20)
-print(ruamel.yaml.dump(data, Dumper=ruamel.yaml.RoundTripDumper), end='')
+yaml.dump(data, sys.stdout)
diff --git a/example/anchor_merge.py b/_example/anchor_merge.py
index 3db92d9..1e52204 100644
--- a/example/anchor_merge.py
+++ b/_example/anchor_merge.py
@@ -1,4 +1,4 @@
-import ruamel.yaml
+from ruamel.yaml import YAML
inp = """\
- &CENTER {x: 1, y: 2}
@@ -24,5 +24,6 @@ inp = """\
label: center/big
"""
-data = ruamel.yaml.load(inp, ruamel.yaml.RoundTripLoader)
+yaml = YAML()
+data = yaml.load(inp)
assert data[7]['y'] == 2
diff --git a/_example/map_insert.py b/_example/map_insert.py
new file mode 100644
index 0000000..ab3486a
--- /dev/null
+++ b/_example/map_insert.py
@@ -0,0 +1,13 @@
+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)
diff --git a/_example/small.py b/_example/small.py
new file mode 100644
index 0000000..2dd72f9
--- /dev/null
+++ b/_example/small.py
@@ -0,0 +1,16 @@
+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)
diff --git a/_example/small_o.py b/_example/small_o.py
new file mode 100644
index 0000000..a50818f
--- /dev/null
+++ b/_example/small_o.py
@@ -0,0 +1,21 @@
+from __future__ import print_function
+
+import sys
+import ruamel.yaml
+
+inp = """\
+# example
+name:
+ # details
+ family: Smith # very common
+ given: Alice # one of the siblings
+"""
+
+code = ruamel.yaml.load(inp, ruamel.yaml.RoundTripLoader)
+code['name']['given'] = 'Bob'
+
+ruamel.yaml.dump(code, sys.stdout, Dumper=ruamel.yaml.RoundTripDumper)
+
+# the last statement can be done less efficient in time and memory with
+# leaving out the end='' would cause a double newline at the end
+# print(ruamel.yaml.dump(code, Dumper=ruamel.yaml.RoundTripDumper), end='')
diff --git a/example/so_10241882.py b/_example/so_10241882.py
index 0dbcb0c..0dbcb0c 100644
--- a/example/so_10241882.py
+++ b/_example/so_10241882.py
diff --git a/example/so_13517753.py b/_example/so_13517753.py
index af1fc2a..af1fc2a 100644
--- a/example/so_13517753.py
+++ b/_example/so_13517753.py
diff --git a/_example/transform.py b/_example/transform.py
new file mode 100644
index 0000000..c88b0c9
--- /dev/null
+++ b/_example/transform.py
@@ -0,0 +1,39 @@
+
+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 = 4
+yaml.block_seq_indent = 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)
diff --git a/_test/test_api_change.py b/_test/test_api_change.py
index 10997f4..9a31655 100644
--- a/_test/test_api_change.py
+++ b/_test/test_api_change.py
@@ -1,12 +1,16 @@
# coding: utf-8
+from __future__ import print_function
+
"""
testing of anchors and the aliases referring to them
"""
+import sys
import pytest
from ruamel.yaml import YAML
from ruamel.yaml.constructor import DuplicateKeyError
+from ruamel.std.pathlib import Path
class TestNewAPI:
@@ -26,3 +30,62 @@ class TestNewAPI:
yaml = YAML(typ='safe')
with pytest.raises(DuplicateKeyError):
yaml.load('{a: 1, a: 2}')
+
+
+class TestWrite:
+ def test_dump_path(self, tmpdir):
+ 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):
+ 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):
+ 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):
+ 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)
+
+ def test_transform(self, tmpdir):
+ def tr(s):
+ 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):
+ 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"
diff --git a/example/small.py b/example/small.py
deleted file mode 100644
index 046d692..0000000
--- a/example/small.py
+++ /dev/null
@@ -1,16 +0,0 @@
-from __future__ import print_function
-
-import ruamel.yaml
-
-inp = """\
-# example
-name:
- # details
- family: Smith # very common
- given: Alice # one of the siblings
-"""
-
-code = ruamel.yaml.load(inp, ruamel.yaml.RoundTripLoader)
-code['name']['given'] = 'Bob'
-
-print(ruamel.yaml.dump(code, Dumper=ruamel.yaml.RoundTripDumper), end='')
diff --git a/main.py b/main.py
index 51c9c95..9cf2edd 100644
--- a/main.py
+++ b/main.py
@@ -298,32 +298,34 @@ class YAML(object):
return loader, loader
return self.constructor, self.parser
- def dump(self, data, stream=None):
- # type: (Any, StreamType) -> Any
- return self.dump_all([data], stream)
+ def dump(self, data, stream, _kw=enforce, transform=None):
+ # type: (Any, StreamType, Any, Any) -> Any
+ return self.dump_all([data], stream, _kw, transform=transform)
- def dump_all(self, documents, stream=None):
- # type: (Any, StreamType) -> Any
+ def dump_all(self, documents, stream, _kw=enforce, transform=None):
+ # type: (Any, StreamType, Any, Any) -> Any
"""
Serialize a sequence of Python objects into a YAML stream.
If stream is None, return the produced string instead.
"""
- # The stream should have the methods `write` and possibly `flush`.
if not hasattr(stream, 'write') and hasattr(stream, 'open'):
# pathlib.Path() instance
with stream.open('w') as fp: # type: ignore
- return self.dump_all(documents, fp)
- getvalue = None
+ return self.dump_all(documents, fp, _kw, transform=transform)
+ if _kw is not enforce:
+ raise TypeError("{}.dump(_all) takes two positional argument but at least "
+ "three were given ({!r})".format(self.__class__.__name__, _kw))
+ # The stream should have the methods `write` and possibly `flush`.
if self.top_level_colon_align is True:
tlca = max([len(str(x)) for x in documents[0]]) # type: Any
else:
tlca = self.top_level_colon_align
- if stream is None:
+ if transform is not None:
+ fstream = stream
if self.encoding is None:
stream = StringIO()
else:
stream = BytesIO()
- getvalue = stream.getvalue
serializer, representer, emitter = \
self.get_serializer_representer_emitter(stream, tlca)
try:
@@ -343,8 +345,8 @@ class YAML(object):
# self.dumper.dispose() # cyaml
delattr(self, "_serializer")
delattr(self, "_emitter")
- if getvalue is not None:
- return getvalue()
+ if transform:
+ fstream.write(transform(stream.getvalue()))
return None
def get_serializer_representer_emitter(self, stream, tlca):
diff --git a/tox.ini b/tox.ini
index 7fe738a..0fcce23 100644
--- a/tox.ini
+++ b/tox.ini
@@ -9,6 +9,7 @@ commands =
deps =
pytest
flake8==2.5.5
+ ruamel.std.pathlib
[pytest]
norecursedirs = test/lib .tox