I would like some help on dumping a custom class object to a YAML file. A representation of my class looks like below:
from classes.enum_classes import Priority, Deadline
class Test(yaml.YAMLObject):
yaml_tag = u'!RandomTestClass'
def __init__(self):
self._name = ""
self._request_id = ""
self._order = None
self._priority = Priority.medium
self._deadline = Deadline.label_c
The last two parameters are objects of different classes, both of which are Enum
derived classes. I am trying to dump the contents of an object of Test
class to a YAML output file. My __repr__
method for the Test
class looks so:
def __repr__(self):
return "(name=%r, request=%r, order=%r, priority=%r, deadline=%r)" % \
((str(self.name) + str(self.order)), self.request_id, self.order,
self.priority.name, self._deadline.name)
Working off the constructors and representers section of the PyYAML documentation and the example provided there (especially considering that the authors use lists for some of the class variables), I would expect my YAML file to display the actual display tags in the __repr__
method, rather than the variable names themselves. This is what I see currently:
--- !ContainerClass
_requests: !!python/object/apply:collections.defaultdict
args:
- !!python/name:builtins.list ''
dictitems:
'108':
- !RandomTestClass
_deadline: &id002 !Deadline '3'
_name: '108.1'
_order: '1'
_priority: &id006 !Priority '1'
_request_id: '108'
- !RandomTestClass
_deadline: &id002 !Deadline '3'
_name: '108.2'
_order: '2'
_priority: &id006 !Priority '1'
_request_id: '108'
_name: TestContainer
I want it to look like so:
---
Requests:
- name: '108.1'
- requestID: '108'
- priority: '1'
- order: '1'
- deadline: '3'
- name: '108.2' <for the second entry in the list and so on>
Name: ContainerClass
No amount of fiddling around with the __repr__
method or anything else has resulted in the output I would like. So there are two issues I would love to get some help with.
How do I get a sane, readable representation? I am guessing I will have to write a custom representer, so if someone could guide me with some pointers, since I was not able to find much information on that any way.
Getting rid of those pointers, or whatever we would want to call them next to priority and deadline. Priority and Deadline are the two classes referenced in my
__init___
method above, that areEnum
subclasses. Since they are already subclasses, any attempts to subclass them toyaml.YAMLObject
result in an error with mixins. To get around that, some posts suggested I do so:
class Priority(Enum):
low = 0
medium = 1
high = 2
class Deadline(Enum):
label_a = 0
label_b = 1
label_c = 2
label_d = 3
def priority_enum_representer(dumper, data):
return dumper.represent_scalar('!Priority', str(data.value))
def deadline_enum_representer(dumper, data):
return dumper.represent_scalar('!Deadline', str(data.value))
yaml.add_representer(Deadline, deadline_enum_representer)
yaml.add_representer(Priority, priority_enum_representer)
Any information/pointers on solving these two issues will be much much appreciated. Apologies for the long post, but I have learnt that more information generally leads to much more precise help.
UPDATE:
My YAML file is written based on a list of these RandomTestClass
objects that are stored in a defaultdict(list)
in a ContainerClass
.
class ContainerClass(yaml.YAMLObject):
yaml_tag = u'ContainerClass'
def __init__(self):
self._name = ""
self._requests = defaultdict(list)
def __repr__(self):
return "(Name=%r, Requests=%r)" % \
(self._name, str(self._requests))
@property
def requests(self):
return self._requests
@requests.setter
def requests(self, new_req, value=None):
if type(new_req) is dict:
self._requests = new_req
else:
try:
self._requests[new_req].append(value)
except AttributeError:
# This means the key exists, but the value is a non-list
# entity. Change existing value to list, append new value
# and reassign to the same key
list_with_values \
= [self._requests[new_req], value]
self._requests[new_req] = list_with_values
The ContainerClass
holds instances of Test
objects. In another class, which is the entry point for my code containing __main__
, I create multiple instances of Test
objects, that are then stored in an ```ContainerClass`` object and dumped out to the YAML file.
# requisite imports here, based on
# how the files are stored
from classes.Test import Test
from classes.ContainerClass import ContainerClass
class RunTestClass:
if __name__ == '__main__':
yaml_container = ContainerClass()
test_object_a = Test()
test_object_a._name = '108.1'
test_object_a._order = 1
test_object_a._request_id = '108'
yaml_container._name = "TestContainer"
yaml_container._requests[test_object_a._request_id] = test_object_a
test_object_b = Test()
test_object_b._name = '108.2'
test_object_b._order = 2
test_object_b._request_id = '108'
yaml_container._name = "TestContainer"
yaml_container._requests[test_object_b._request_id] = test_object_b
with open(output_file, mode='w+') as outfile:
for test_class_object in yaml_container._requests:
yaml.dump(test_class_object, outfile, default_flow_style=False,
explicit_start=True, explicit_end=True)
UPDATE:
Adding a single, consolidated file to the question, executable to replicate the issue.
import yaml
from enum import Enum
from collections import defaultdict
class Priority(Enum):
low = 0
medium = 1
high = 2
class Deadline(Enum):
label_a = 0
label_b = 1
label_c = 2
label_d = 3
def priority_enum_representer(dumper, data):
return dumper.represent_scalar('!Priority', str(data.value))
def deadline_enum_representer(dumper, data):
return dumper.represent_scalar('!Deadline', str(data.value))
yaml.add_representer(Deadline, deadline_enum_representer)
yaml.add_representer(Priority, priority_enum_representer)
class Test(yaml.YAMLObject):
yaml_tag = u'!RandomTestClass'
def __init__(self):
self._name = ""
self._request_id = ""
self._order = None
self._priority = Priority.medium
self._deadline = Deadline.label_c
@property
def name(self):
return self._name
@name.setter
def name(self, name):
self._name = name
@property
def request_id(self):
return self._request_id
@request_id.setter
def request_id(self, r_id):
self._request_id = r_id
@property
def order(self):
return self._order
@order.setter
def order(self, order):
self._order = order
@property
def priority(self):
return self._priority
@priority.setter
def priority(self, priority):
self._priority = priority
@property
def deadline(self):
return self._deadline
@deadline.setter
def deadline(self, deadline):
self._deadline = deadline
def __str__(self):
return self.name + ", " + self._request_id + ", " + str(self.order) + ", " \
+ str(self.priority) + ", " + str(self.deadline)
class ContainerClass(yaml.YAMLObject):
yaml_tag = u'ContainerClass'
def __init__(self):
self._name = ""
self._requests = defaultdict(list)
def __repr__(self):
return "(Name=%r, Requests=%r)" % \
(self._name, str(self._requests))
@property
def name(self):
return self._name
@name.setter
def name(self, name):
self._name = name
@property
def requests(self):
return self._requests
def set_requests(self, new_req, value=None):
if type(new_req) is dict:
self._requests = new_req
else:
try:
self._requests[new_req].append(value)
except AttributeError:
# This means the key exists, but the value is a non-list
# entity. Change existing value to list, append new value
# and reassign to the same key
print("Encountered a single value, converting to a list and appending new value")
list_with_values \
= [self._requests[new_req], value]
self._requests[new_req] = list_with_values
yaml_container = ContainerClass()
yaml_container.name = "TestContainer"
test_object_a = Test()
test_object_a._name = '108.1'
test_object_a._order = 1
test_object_a._request_id = '108'
yaml_container.set_requests(test_object_a.request_id, test_object_a)
test_object_b = Test()
test_object_b._name = '108.2'
test_object_b._order = 2
test_object_b._request_id = '108'
yaml_container.set_requests(test_object_b.request_id, test_object_b)
with open('test.yaml', mode='w+') as outfile:
yaml.dump(yaml_container, outfile, default_flow_style=False,
explicit_start=True, explicit_end=True)
...` and that's far from what you show. Try to enable me run your code. – flyx Jan 21 '21 at 23:54