Pyyaml: ConstructorError - could not determine a constructor for custom tag (5.1)

Created on 14 Mar 2019  路  23Comments  路  Source: yaml/pyyaml

The following code was working on 3.13 but no longer works in 5.1:

import yaml

class Ref(yaml.YAMLObject):
    yaml_tag = '!Ref'
    def __init__(self, val):
        self.val = val

    @classmethod
    def from_yaml(cls, loader, node):
        return cls(node.value)

yaml.load('Foo: !Ref bar')

I get the following exception on 5.1:

yaml.constructor.ConstructorError: could not determine a constructor for the tag '!Ref'
  in "<unicode string>", line 1, column 6:
    Foo: !Ref bar

This is true whether I use yaml.load, yaml.full_load or yaml.unsafe_load.

Most helpful comment

Try

yaml.load('Foo: !Ref bar', Loader=yaml.Loader)

I'm not sure though if this is intentional.
Will have a closer look later.

All 23 comments

Try

yaml.load('Foo: !Ref bar', Loader=yaml.Loader)

I'm not sure though if this is intentional.
Will have a closer look later.

It works with Loader=yaml.Loader. Thanks!

ok, I think I see it now. From https://github.com/yaml/pyyaml/blob/e471e86bf6dabdad45a1438c20a4a5c033eb9034/lib/yaml/__init__.py#L386, the loader for metaclass-driven custom tags defaults to yaml.Loader. The following also works for me:

import yaml

class Ref(yaml.YAMLObject):
    yaml_loader = yaml.SafeLoader
    yaml_tag = '!Ref'
    def __init__(self, val):
        self.val = val

    @classmethod
    def from_yaml(cls, loader, node):
        return cls(node.value)

yaml.safe_load('Foo: !Ref bar')

safe_load doesn't work for me but Loader=yaml.Loader does.

Today I encountered the same problem. Even the example straight from the documentation on the homepage does not work anymore

#!/usr/bin/env python
import yaml

class Monster(yaml.YAMLObject):
    yaml_tag = u'!Monster'
    def __init__(self, name, hp, ac, attacks):
        self.name = name
        self.hp = hp
        self.ac = ac
        self.attacks = attacks
    def __repr__(self):
        return "%s(name=%r, hp=%r, ac=%r, attacks=%r)" % (
            self.__class__.__name__, self.name, self.hp, self.ac, self.attacks)

yaml.unsafe_load("""
--- !Monster
name: Cave spider
hp: [2,6]    # 2d6
ac: 16
attacks: [BITE, HURT]
""")

Backtrace:

Traceback (most recent call last):
  File "./testfile", line 21, in <module>
    """)
  File "/home/user/.local/lib/python2.7/site-packages/yaml/__init__.py", line 182, in unsafe_load
    return load(stream, UnsafeLoader)
  File "/home/user/.local/lib/python2.7/site-packages/yaml/__init__.py", line 114, in load
    return loader.get_single_data()
  File "/home/user/.local/lib/python2.7/site-packages/yaml/constructor.py", line 45, in get_single_data
    return self.construct_document(node)
  File "/home/user/.local/lib/python2.7/site-packages/yaml/constructor.py", line 49, in construct_document
    data = self.construct_object(node)
  File "/home/user/.local/lib/python2.7/site-packages/yaml/constructor.py", line 94, in construct_object
    data = constructor(self, node)
  File "/home/user/.local/lib/python2.7/site-packages/yaml/constructor.py", line 420, in construct_undefined
    node.start_mark)
yaml.constructor.ConstructorError: could not determine a constructor for the tag '!Monster'
  in "<string>", line 2, column 5:
    --- !Monster
        ^

Edit:
This seems to work (why?)

yaml.load(..., Loader=yaml.Loader)

Yeah, the problem is that the default loader for load is now FullLoader, but the YAMLObject still uses yaml.Loader. #271 is similar.
We are discussing what to do.

I created #273

@kgutwin, i am facing same issue:

Python 2.7
Pyyaml: 5.1

Error: yaml.constructor.ConstructorError: could not determine a constructor for the tag 'tag:yaml.org,2002:python/name:pymdownx.emoji.to_svg'

new_file.yml

extensions:
  - pymdownx.emoji:
      emoji_generator: !regexp:pymdownx.emoji.to_svg

parse.py

import re
yaml.SafeLoader.add_constructor(u'!regexp', lambda l, n: re.compile(l.construct_scalar(n)))
with open("new_file.yml", 'r') as stream:
    print yaml.load(stream)

This is a breaking change, and is likely to break a ton of code out there that worked just fine: I spent myself a whole afternoon trying to figure out what I'd changed that broke all my tests.

Are you guys going to revert changes, or provide a better error message (at the very least)?

yaml.load('Foo: !Ref bar', Loader=yaml.Loader) does not work in Python 3.6.8, Pyaml 5.1.1, what is the current workaround?

yaml.load('Foo: !Ref bar', Loader=yaml.Loader) does not work in Python 3.6.8, Pyaml 5.1.1, what is the current workaround?

It worked like a charm for my
(_after a day of trial and researching about the issue_)
it saved my day...

Python 3.6.9
PyYAML==5.1.2

We just released 5.2b1 https://pypi.org/project/PyYAML/5.2b1/ which should fix this.

None of the workarounds above worked for me, I had to use this loader:
Loader=yaml.BaseLoader
on
Python 3.7.0
PyYaml==5.1.2 or == 5.2b1

_(edited)_
I reproduced using the same code: yaml.load('Foo: !Ref bar')
This worked for me :yaml.load('Foo: !Ref bar', Loader=yaml.BaseLoader)

@maru-podmast do you have some code to reproduce?

PS- this seems to work fine for me as of 5.2b1:

[root@1ee3dc7c3d7f pyyaml]# /opt/python/cp37-cp37m/bin/python
Python 3.7.5 (default, Nov 27 2019, 12:25:51) 
[GCC 8.3.1 20190311 (Red Hat 8.3.1-3)] on linux
Type "help", "copyright", "credits" or "license" for more information.
>>> import yaml
>>> 
>>> class Monster(yaml.YAMLObject):
...     yaml_tag = u'!Monster'
...     def __init__(self, name, hp, ac, attacks):
...         self.name = name
...         self.hp = hp
...         self.ac = ac
...         self.attacks = attacks
...     def __repr__(self):
...         return "%s(name=%r, hp=%r, ac=%r, attacks=%r)" % (
...             self.__class__.__name__, self.name, self.hp, self.ac, self.attacks)
... 
>>> yaml.unsafe_load("""
... --- !Monster
... name: Cave spider
... hp: [2,6]    # 2d6
... ac: 16
... attacks: [BITE, HURT]
... """)
Monster(name='Cave spider', hp=[2, 6], ac=16, attacks=['BITE', 'HURT'])
>>>

@maru-podmast sorry, I can't reproduce. the original code works for me with 5.2b1 and 5.2:

yaml.load('Foo: !Ref bar', Loader=yaml.Loader)
yaml.load('Foo: !Ref bar', Loader=yaml.FullLoader)
yaml.load('Foo: !Ref bar') # emits warning

Released 5.2. Please reopen if necessary

@perlpunk This isn't working for me using the example you provided. I'm seeing the same results as @maru-podmast. What are we missing? It does work with BaseLoader.

>>> yaml.__version__
'5.2'
>>> yaml.load('Foo: !Ref bar', Loader=yaml.Loader)
Traceback (most recent call last):
...
... 
... /python3.7/site-packages/yaml/constructor.py", line 420, in construct_undefined
    node.start_mark)
yaml.constructor.ConstructorError: could not determine a constructor for the tag '!Ref'
  in "<unicode string>", line 1, column 6:
    Foo: !Ref bar

I'm on Python 3.7.4

@BonnieH @maru-podmast
I can only reproduce this without using the original code at the top of the issue.
Can you execute this code and show the output please?

import yaml

class Ref(yaml.YAMLObject):
    yaml_tag = '!Ref'
    def __init__(self, val):
        self.val = val

    @classmethod
    def from_yaml(cls, loader, node):
        return cls(node.value)

print(yaml.__version__)
data = yaml.load('Foo: !Ref bar', yaml.Loader)
print(data)
print(data['Foo'].val)

Without that class Ref of course it can't work. BaseLoader simply ignores the unknown tags.

@perlpunk This was a misunderstanding on my part, I hadn't defined the Ref class in my example. So it works now! And my real code is working now too, thanks for your help.

I'm using version 5.3 like so:

import yaml

yaml.load("mgnl:variationOf: !weakreference 721ec351-e73a-489e-b467-e9d608468ebd", Loader=yaml.Loader)

But I still got the same error.

yaml.constructor.ConstructorError: could not determine a constructor for the tag '!weakreference'
  in "<unicode string>", line 1, column 19:
    mgnl:variationOf: !weakreference 721ec351-e73a-489 ...

I resolved the issue by following https://github.com/yaml/pyyaml/issues/266#issuecomment-559116876.

yaml.load("mgnl:variationOf: !weakreference 721ec351-e73a-489e-b467-e9d608468ebd", Loader=yaml.BaseLoader)

@ryanbrookepayne BaseLoader only works because it is ignoring the weakreference tag.
To do it correctly, you would have to use the loader for which you added the constructor.

Same error for me. Worked using:

yaml.load('Foo: !Ref bar', Loader=yaml.BaseLoader)

Python 3.8.2
PyYAML 5.3.1

Was this page helpful?
0 / 5 - 0 ratings

Related issues

IRDonch picture IRDonch  路  8Comments

graingert picture graingert  路  7Comments

lucj picture lucj  路  7Comments

jeoffridavis picture jeoffridavis  路  4Comments

ingydotnet picture ingydotnet  路  4Comments