Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

ConstructorError - could not determine a constructor for custom tag (5.1) #266

Closed
kgutwin opened this issue Mar 14, 2019 · 29 comments
Closed

Comments

@kgutwin
Copy link

kgutwin commented Mar 14, 2019

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.

@kgutwin kgutwin changed the title Problems with custom tags in 5.1 ConstructorError - could not determine a constructor for custom tag (5.1) Mar 14, 2019
@perlpunk
Copy link
Member

Try

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

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

@kgutwin
Copy link
Author

kgutwin commented Mar 14, 2019

It works with Loader=yaml.Loader. Thanks!

@kgutwin
Copy link
Author

kgutwin commented Mar 14, 2019

ok, I think I see it now. From

yaml_loader = Loader
, 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')

@gitmlynch
Copy link

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

@Rayman
Copy link

Rayman commented Mar 15, 2019

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)

@perlpunk
Copy link
Member

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.

@perlpunk
Copy link
Member

I created #273

@akhilcalsoft
Copy link

akhilcalsoft commented May 15, 2019

@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)

@massenz
Copy link

massenz commented Jul 15, 2019

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)?

@andrewsav-bt
Copy link

andrewsav-bt commented Oct 10, 2019

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?

@Wolfium
Copy link

Wolfium commented Oct 12, 2019

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

@perlpunk
Copy link
Member

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

@rmarou
Copy link

rmarou commented Nov 27, 2019

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)

@perlpunk
Copy link
Member

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

@nitzmahone
Copy link
Member

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'])
>>>

@perlpunk
Copy link
Member

perlpunk commented Dec 2, 2019

@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

@perlpunk
Copy link
Member

perlpunk commented Dec 2, 2019

Released 5.2. Please reopen if necessary

@perlpunk perlpunk closed this as completed Dec 2, 2019
@BonnieH
Copy link

BonnieH commented Dec 4, 2019

@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

@perlpunk
Copy link
Member

perlpunk commented Dec 4, 2019

@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.

@BonnieH
Copy link

BonnieH commented Dec 4, 2019

@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.

@ryanbrookepayne
Copy link

ryanbrookepayne commented Mar 18, 2020

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 #266 (comment).

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

@perlpunk
Copy link
Member

@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.

@turanzv
Copy link

turanzv commented Nov 23, 2020

Same error for me. Worked using:

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

Python 3.8.2
PyYAML 5.3.1

AdamI75 added a commit to ska-sa/mlib_devel that referenced this issue Jan 29, 2021
The requirements.txt file has been updated with the "numpy<1.19". Version 1.18.5was the last to support Python 3.5, which we are using. The newer version causes conflicts. Also castro.py causes constructor error due to python pyyaml updates. This was working with pyyaml version 3.13, but not version 5.1 and above. It suggested that I add "Loader=yaml.Loader" to the yaml.load() function and it no longer breaks - checkout yaml/pyyaml#266.
jack-h pushed a commit to casper-astro/mlib_devel that referenced this issue Feb 9, 2021
The requirements.txt file has been updated with the "numpy<1.19". Version 1.18.5was the last to support Python 3.5, which we are using. The newer version causes conflicts. Also castro.py causes constructor error due to python pyyaml updates. This was working with pyyaml version 3.13, but not version 5.1 and above. It suggested that I add "Loader=yaml.Loader" to the yaml.load() function and it no longer breaks - checkout yaml/pyyaml#266.
sivanoff added a commit to sivanoff/rclone-changer that referenced this issue Feb 21, 2021
@ahseena96
Copy link

ahseena96 commented Jul 15, 2021

Same error for me. Worked using:

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

Python 3.8.2
PyYAML 5.3.1

Python 3.6.10
PyYAML 5.3.1

The error is resolved when I use yaml.BaseLoader. However, !Ref is ignored and missing in the loaded content.
It won't work without class Ref, like @perlpunk mentioned.

@black-snow
Copy link

Still seems to be true in mid 2022.

@vallamost
Copy link

Is there a workaround for ignoring the tags / refs when parsing?

@black-snow
Copy link

@vallamost I think the yaml.BaseLoader ignores 'em

@perlpunk
Copy link
Member

perlpunk commented Jul 27, 2022

To ignore all tags (not yet defined) in SafeLoader:

def ignore(loader, tag, node):
    classname = node.__class__.__name__
    if (classname == 'SequenceNode'):
        resolved = loader.construct_sequence(node)
    elif (classname == 'MappingNode'):
        resolved = loader.construct_mapping(node)
    else:
        resolved = loader.construct_scalar(node)
    return resolved

yaml.add_multi_constructor('!', ignore, Loader=yaml.SafeLoader)
yaml.add_multi_constructor('', ignore, Loader=yaml.SafeLoader)

@pykeras
Copy link

pykeras commented Mar 18, 2023

Here is the solution which worked for me:

class Ref(yaml.YAMLObject):
  yaml_tag = '!Ref'
  yaml_loader = yaml.SafeLoader
  def __init__(self, value):
    self.value = value
    
  def __repr__(self):
    return f'{type(self).__name__}(value={self.value!r})'
  
# print(yaml.dump({'Foo': Ref('bar')}, default_flow_style=False))
  
yaml.load('''Foo: !Ref
  value: bar''', Loader=yaml.SafeLoader)

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging a pull request may close this issue.