pyyaml load number as decimal

Viewed 1571

yaml.load loads numbers as Python floats. I cannot find a straightforward way to override this.

Compare json.load, which allows parse_float=Decimal if you want to parse floating point numbers as decimal.Decimals.

Is there any way to accomplish this with PyYAML? Or is this inadvisable by some property of the YAML spec?

1 Answers

You can do something like:

def decimal_constructor(loader, node):
    value = loader.construct_scalar(node)
    return Decimal(value)

yaml.add_constructor(u'!decimal', decimal_constructor)

This allows you to load decimals, but only if they are prefixed with the !decimal tag in the YAML document. However, you can use a custom Resolver to resolve all numbers to !decimal:

class MyResolver(BaseResolver):
    pass

MyResolver.add_implicit_resolver(
        '!decimal',
        re.compile(r'''^(?:[-+]?(?:[0-9][0-9_]*)\.[0-9_]*(?:[eE][-+][0-9]+)?
                    |\.[0-9_]+(?:[eE][-+][0-9]+)?
                    |[-+]?[0-9][0-9_]*(?::[0-5]?[0-9])+\.[0-9_]*
                    |[-+]?\.(?:inf|Inf|INF)
                    |\.(?:nan|NaN|NAN))$''', re.X),
        list('-+0123456789.'))

You should copy the other implicit resolvers over from the default resolver. Then, you need to define a Loader that uses your resolver.

class MyLoader(Reader, Scanner, Parser, Composer, SafeConstructor, MyResolver):
    def __init__(self, stream):
        Reader.__init__(self, stream)
        Scanner.__init__(self)
        Parser.__init__(self)
        Composer.__init__(self)
        SafeConstructor.__init__(self)
        MyResolver.__init__(self)

Above, we added your constructor to the default loader. Change that line to:

yaml.add_constructor(u'!decimal', decimal_constructor, MyLoader)

Finally, load the YAML using

yaml.load(stream, MyLoader)
Related