summaryrefslogtreecommitdiff
path: root/_doc
diff options
context:
space:
mode:
authorAnthon van der Neut <anthon@mnt.org>2017-07-13 23:32:42 +0200
committerAnthon van der Neut <anthon@mnt.org>2017-07-13 23:32:42 +0200
commiteaba7badcb3ce04a9db6e4fe8504950cef832f7d (patch)
treeb6fefde9db338bf03ae0569ad9c4d84bd96aa1ac /_doc
parent2ea4de43fe2e0160e27b605f2c65f33c5a2083d7 (diff)
downloadruamel.yaml-eaba7badcb3ce04a9db6e4fe8504950cef832f7d.tar.gz
added register_class/yaml_object0.15.19
Diffstat (limited to '_doc')
-rw-r--r--_doc/api.rst76
-rw-r--r--_doc/api.ryd269
-rw-r--r--_doc/basicuse.rst45
-rw-r--r--_doc/basicuse.ryd78
-rw-r--r--_doc/dumpcls.rst97
-rw-r--r--_doc/dumpcls.ryd106
-rw-r--r--_doc/index.rst1
7 files changed, 610 insertions, 62 deletions
diff --git a/_doc/api.rst b/_doc/api.rst
index f6ff28c..7f34c93 100644
--- a/_doc/api.rst
+++ b/_doc/api.rst
@@ -1,5 +1,3 @@
-
-
Departure from previous API
---------------------------
@@ -35,23 +33,24 @@ of ``YAML`` before calling ``load()`` or ``dump()``
Before 0.15.0::
- 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)
+ 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::
- 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)
+ 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 an keyword argument ``explicit_start=True`` you
now do ``yaml.explicit_start = True`` before calling ``dump()``. The
@@ -63,6 +62,12 @@ 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 (`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``
@@ -76,7 +81,6 @@ all functionality of the old interface will be available via
Loading
-------
-
Duplicate keys
++++++++++++++
@@ -127,55 +131,53 @@ for reading resp. writing.
Loading and dumping using the ``SafeLoader``::
- if yaml.version_info < (0, 15):
+ if ruamel.yaml.version_info < (0, 15):
data = yaml.safe_load(istream)
yaml.safe_dump(data, ostream)
else:
- yml = yaml.YAML(typ='safe', pure=True) # 'safe' load and dump
+ yml = ruamel.yaml.YAML(typ='safe', pure=True) # 'safe' load and dump
data = yml.load(istream)
- yml.dump(ostream)
-
+ yml.dump(data, ostream)
Loading with the ``CSafeLoader``, dumping with
``RoundTripLoader``. You need two ``YAML`` instances, but each of them
-can be re-used ::
+can be re-used::
- if yaml.version_info < (0, 15):
+ 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 = yaml.YAML(typ='safe')
+ yml = ruamel.yaml.YAML(typ='safe')
data = yml.load(istream)
- ymlo = yaml.YAML() # or yaml.YAML(typ='rt')
+ ymlo = ruamel.yaml.YAML() # or yaml.YAML(typ='rt')
ymlo.width = 1000
ymlo.explicit_start = True
- ymlo.dump(ostream)
-
+ ymlo.dump(data, ostream)
Loading and dumping from ``pathlib.Path`` instances using the
round-trip-loader::
# in myyaml.py
- if yaml.version_info < (0, 15):
+ if ruamel.yaml.version_info < (0, 15):
class MyYAML(yaml.YAML):
def __init__(self):
yaml.YAML.__init__(self)
self.preserve_quotes = True
self.indent = 4
self.block_seq_indent = 2
-
+ # in your code
try:
from myyaml import MyYAML
- except ImportError:
- if yaml.version_info >= (0, 15):
+ 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 yaml.version_info < (0, 15):
+
+ 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:
@@ -184,10 +186,7 @@ round-trip-loader::
yml = MyYAML()
# no need for with statement when using pathlib.Path instances
data = yml.load(inf)
- yml.dump(outf)
-
-
-
+ yml.dump(data, outf)
Reason for API change
---------------------
@@ -242,3 +241,4 @@ 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/api.ryd b/_doc/api.ryd
new file mode 100644
index 0000000..24f06e6
--- /dev/null
+++ b/_doc/api.ryd
@@ -0,0 +1,269 @@
+---
+version: 0.1
+output: rst
+fix_inline_single_backquotes: true
+pdf: true
+--- !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 inhereted 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 stram argument. All other parameters are set on the instance
+of ``YAML`` before calling ``load()`` or ``dump()``
+
+Before 0.15.0::
+--- !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 an 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 (`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).
+
+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.
+
+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
+------------------------------------
+
+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``::
+
+--- !python |
+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 = 4
+ self.block_seq_indent = 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()`` a
+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.rst b/_doc/basicuse.rst
index 56a28d6..f99d609 100644
--- a/_doc/basicuse.rst
+++ b/_doc/basicuse.rst
@@ -9,11 +9,10 @@ the process of being fleshed out*. Please pin your dependency to
You load a YAML document using::
- from ruamel.yaml import YAML
-
- yaml=YAML(typ='safe') # default if not specfied is round-trip
-
- yaml.load(doc)
+ from ruamel.yaml import YAML
+
+ yaml=YAML(typ='safe') # default if not specfied is 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'`
@@ -24,11 +23,11 @@ when possible/available)
Dumping works in the same way::
- from ruamel.yaml import YAML
-
- yaml=YAML()
- yaml.default_flow_style = False
- yaml.dump({a: [1, 2]}, s)
+ 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
@@ -37,26 +36,24 @@ 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)
-
+ 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`` load as integer ``42``)::
- from ruamel.yaml import YAML
-
- yaml=YAML(typ="safe")
- yaml.load("""a:\n b: 2\n c: 3\n""")
+ 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``)::
- from ruamel.yaml import YAML
-
- yaml=YAML(typ="safe", pure=True)
- yaml.load("""a:\n b: 2\n c: 3\n""")
+ from ruamel.yaml import YAML
+
+ yaml=YAML(typ="safe", pure=True)
+ yaml.load("""a:\n b: 2\n c: 3\n""")
diff --git a/_doc/basicuse.ryd b/_doc/basicuse.ryd
new file mode 100644
index 0000000..7d3ae96
--- /dev/null
+++ b/_doc/basicuse.ryd
@@ -0,0 +1,78 @@
+---
+version: 0.1
+output: rst
+fix_inline_single_backquotes: true
+pdf: true
+--- !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
+===========
+
+*This is the new (0.15+) interface for ``ruamel.yaml``, it is still in
+the process of being fleshed out*. Please pin your dependency to
+``ruamel.yaml<0.15`` for production software.
+
+------
+
+You load a YAML document using::
+--- !python |
+from ruamel.yaml import YAML
+
+yaml=YAML(typ='safe') # default if not specfied is 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 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::
+--- !python |
+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`` load 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""") \ No newline at end of file
diff --git a/_doc/dumpcls.rst b/_doc/dumpcls.rst
new file mode 100644
index 0000000..0195689
--- /dev/null
+++ b/_doc/dumpcls.rst
@@ -0,0 +1,97 @@
+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.
+
+If you have instances of some class(es) that you want to dump or load, it is
+easy to allow the YAML instance to that explicitly. You can either registering the
+class with the ``YAML`` instance or decorate the class.
+
+Registering is done with ``YAML.register_class()``::
+
+ import sys
+ import ruamel.yaml
+
+
+ class User(object):
+ 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)
+
+which gives as output::
+
+ - !User
+ name: Anthon
+ age: 18
+
+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 called ``from_yaml`` resp. ``from_yaml``::
+
+ 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)
+
+which gives as output::
+
+ - !user Anthon-18
+
+When using the decorator, which takes the ``YAML()`` instance as a parameter,
+the ``yaml = YAML()`` line needs to be moved up in the file::
+
+ 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()``.
+
diff --git a/_doc/dumpcls.ryd b/_doc/dumpcls.ryd
new file mode 100644
index 0000000..489d1b7
--- /dev/null
+++ b/_doc/dumpcls.ryd
@@ -0,0 +1,106 @@
+---
+version: 0.1
+output: rst
+fix_inline_single_backquotes: true
+pdf: true
+# code_directory: ../_example
+--- |
+
+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.
+
+If you have instances of some class(es) that you want to dump or load, it is
+easy to allow the YAML instance to that explicitly. You can either registering 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(object):
+ 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 called ``from_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()``.
diff --git a/_doc/index.rst b/_doc/index.rst
index 28e3cdc..c9f9659 100644
--- a/_doc/index.rst
+++ b/_doc/index.rst
@@ -14,6 +14,7 @@ Contents:
overview
install
basicuse
+ dumpcls
detail
example
api