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

Loading nested object as key #620

Open
rhelmot opened this issue Mar 6, 2022 · 3 comments
Open

Loading nested object as key #620

rhelmot opened this issue Mar 6, 2022 · 3 comments

Comments

@rhelmot
Copy link

rhelmot commented Mar 6, 2022

The following setup triggers a crash on deserialize:

[+] In [23]: class A:
        ...:     def __init__(self, a):
        ...:         self.a = a
        ...:
        ...:     def __hash__(self):
        ...:         return hash(self.a)
        ...:
        ...:     def __eq__(self, other):
        ...:         return type(other) is type(self) and self.a == other.a
        ...:
        ...:     def __getstate__(self):
        ...:         return {'a': self.a}
        ...:
        ...:     def __setstate__(self, state):
        ...:         self.a = state['a']

[+] In [24]: a = A(1)

[+] In [25]: b = {(a, 1): 1}

[+] In [26]: with open('a.yaml', 'w') as fp:
        ...:     yaml.dump(b, fp)
        ...:

[+] In [27]: with open('a.yaml', 'r') as fp:
        ...:     b = yaml.unsafe_load(fp)
        ...:
---------------------------------------------------------------------------
AttributeError                            Traceback (most recent call last)
<ipython-input-27-672f39e43a2c> in <module>
      1 with open('a.yaml', 'r') as fp:
----> 2     b = yaml.unsafe_load(fp)
      3

~/proj/celeste/Magic/timer/yaml/__init__.py in unsafe_load(stream)
    143     unsafe on untrusted input.
    144     """
--> 145     return load(stream, UnsafeLoader)
    146
    147 def unsafe_load_all(stream):

~/proj/celeste/Magic/timer/yaml/__init__.py in load(stream, Loader)
     79     loader = Loader(stream)
     80     try:
---> 81         return loader.get_single_data()
     82     finally:
     83         loader.dispose()

~/proj/celeste/Magic/timer/yaml/constructor.py in get_single_data(self)
     49         node = self.get_single_node()
     50         if node is not None:
---> 51             return self.construct_document(node)
     52         return None
     53

~/proj/celeste/Magic/timer/yaml/constructor.py in construct_document(self, node)
     58             self.state_generators = []
     59             for generator in state_generators:
---> 60                 for dummy in generator:
     61                     pass
     62         self.constructed_objects = {}

~/proj/celeste/Magic/timer/yaml/constructor.py in construct_yaml_map(self, node)
    411         data = {}
    412         yield data
--> 413         value = self.construct_mapping(node)
    414         data.update(value)
    415

~/proj/celeste/Magic/timer/yaml/constructor.py in construct_mapping(self, node, deep)
    216         if isinstance(node, MappingNode):
    217             self.flatten_mapping(node)
--> 218         return super().construct_mapping(node, deep=deep)
    219
    220     def construct_yaml_null(self, node):

~/proj/celeste/Magic/timer/yaml/constructor.py in construct_mapping(self, node, deep)
    142                         "found unhashable key", key_node.start_mark)
    143             value = self.construct_object(value_node, deep=deep)
--> 144             mapping[key] = value
    145         return mapping
    146

<ipython-input-17-8dbd1d52c935> in __hash__(self)
      4
      5     def __hash__(self):
----> 6         return hash(self.a)
      7
      8     def __eq__(self, other):

AttributeError: 'A' object has no attribute 'a'

I believe this is because mapping keys need to be deep-constructed prior to being inserted into the map. The following diff fixes the issue for me:

         mapping = {}
         for key_node, value_node in node.value:
-            key = self.construct_object(key_node, deep=deep)
+            key = self.construct_object(key_node, deep=True)
             if not isinstance(key, collections.abc.Hashable):
                 raise ConstructorError("while constructing a mapping", node.start_mark
@ivannp
Copy link

ivannp commented Jul 6, 2022

Hit the same when using a frozen dataclass in a set or in a dictionary.

@ivannp
Copy link

ivannp commented Jul 16, 2022

A more complex fix is needed to cover more scenarios. I will create a PR soon.

@ivannp
Copy link

ivannp commented Jul 16, 2022

#654

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

No branches or pull requests

2 participants