Search code examples
pythonpyyaml

PyYAML: How to set a default representer for non-standard objects?


Is it possible to specify a default PyYAML representer for non-standard Python objects?

import yaml
class Foo(int): pass 
class Bar(float): pass

data = dict(foo=Foo(42), bar=Bar(3.14), baz=[1,2])
print(yaml.safe_dump(data))
# Raises error: 
#     RepresenterError: ('cannot represent an object', ...)

print(yaml.dump(data))
# Prints:
#     bar: !!python/object/new:__main__.Bar
#     - 3.14
#     baz:
#     - 1
#     - 2
#     foo: !!python/object/new:__main__.Foo
#     - 42

How to instruct PyYAML to use a default representer for all non-standard objects? For instance, I would like to invoke an object's __str__() or __repr__() method iff it cannot be represented safely.


Solution

  • I was lucky with the following "naive" approach:

    import yaml
    class Foo(int): pass 
    class Bar(float): pass
    data = dict(foo=Foo(42), bar=Bar(3.14), baz=[1,2])
    
    # Set default representer
    def default_representer(dumper, data):
        # Alternatively, use repr() instead str():
        return dumper.represent_scalar('tag:yaml.org,2002:str', str(data))
    yaml.representer.SafeRepresenter.add_representer(None, default_representer)
    
    # Now this works as expected: unknown objects 
    # are printed as strings.
    print(yaml.safe_dump(data))
    # Prints:
    #     bar: '3.14'
    #     baz:
    #     - 1
    #     - 2
    #     foo: '42'
    

    To make this work via yaml.dump() as well, I set the Dumper argument:

    print(yaml.dump(data, Dumper=yaml.SafeDumper))
    

    However, this makes it essentially the same as yaml.safe_dump(). I could not find a better way to disable the type tags.