简体   繁体   中英

Can ruamel.yaml encode an enum?

The following is not working for me, with Python 3.4.7, ruamel.yaml version 0.15.35:

import sys
import enum

import ruamel.yaml
from ruamel.yaml import yaml_object
yaml = ruamel.yaml.YAML()

@yaml_object(yaml)
class Speed(enum.IntEnum):
    Reverse = 0
    Neutral = 1
    Low = 2
    Drive = 3
    Park = 999

print("Neutral:", repr(Speed.Neutral))

yaml.dump(Speed.Neutral, sys.stdout)

I get a totally reasonable repr :

Neutral: <Speed.Neutral: 1>

but the .dump() raises:

ruamel.yaml.representer.RepresenterError: cannot represent an object: <enum 'Speed'>

If enum 's are not supported, is there something I can do to extend the enum class I am using (or the subclass enum.IntEnum I have created), eg a dunder method?

enum is not supported out-of-the-box, essentially because the default dump method is safe and therefore doesn't support arbitrary Python objects. That safety excludes types as enum from the standard library as well.

What you should do is add a to_yaml classmethod to your Speed class as described in the ruamel.yaml documentation :

import sys
import enum

import ruamel.yaml
from ruamel.yaml import yaml_object
yaml = ruamel.yaml.YAML()

@yaml_object(yaml)
class Speed(enum.IntEnum):
    Reverse = 0
    Neutral = 1
    Low = 2
    Drive = 3
    Park = 999

    @classmethod
    def to_yaml(cls, representer, node):
        return representer.represent_scalar(
            u'!Speed',
            '{}-{}'.format(node._name_, node._value_)
        )

yaml.dump(Speed.Neutral, sys.stdout)

which gives:

!Speed Neutral-1
...

You can of course adapt the string representation to your liking (and add a from_yaml to be able to load the output back in).

Please note that you cannot add yaml_tag as in the documentation example, as this would interfere with the enum values.

I think you'll need to pass the enum by value.

https://docs.python.org/3/library/enum.html#programmatic-access-to-enumeration-members-and-their-attributes

yaml.dump(Speed.Neutral.value, sys.stdout)

Extended answer to add a @classmethod which calls .value as it's being passed

http://yaml.readthedocs.io/en/latest/dumpcls.html

import sys
import enum
import ruamel.yaml
from ruamel.yaml import yaml_object
yaml = ruamel.yaml.YAML()

@yaml_object(yaml)
class Speed(enum.IntEnum):
    Reverse = 0
    Neutral = 1
    Low = 2
    Drive = 3
    Park = 999

    @classmethod
    def to_yaml(cls, representer, node):
        return representer.represent_scalar(u'!enum:', u'{.value}'.format(node))

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM