Question

I am trying to create a yaml format that allow me to create an object inside another object. In this sample i am trying to create a State Machine object and at the same time populate it with some states and connections between them.

yaml.load("""
!statemachine {
     states: [
       !state { name: p1 },
       !state { name: p2 },
       !state { name: p3 },],
     connections:
      [!connection { 'pim' : [p1,p2]}]}
""")

!statemachine have a constructor that generates an object of type MyStateMachine

!state have a constructor that generates an object of type MyState

!connection have a constructor that should use object generated with name p1 and add a connection to it

I have 2 problems here:

1 - The constructor for state is called after the statemachine is created and the states are not present inside it

2 - Retrieve the object p1 and invoke the method add_connection over it.

Thanks in advance

Was it helpful?

Solution

lets try the true pyyaml syntax for objects

myyaml.py:

import yaml,sys

class StateMachine(object):
    pass

class State(object):
    pass

class Connection(object):
    pass

if __name__ == '__main__':
    o = yaml.load("""
    !!python/object:myyaml.StateMachine {
         states: [
           !!python/object:myyaml.State { name: p1 },
           !!python/object:myyaml.State { name: p2 },
           !!python/object:myyaml.State { name: p3 },],
         connections:
          [       !!python/object:myyaml.Connection { 'pim' : [p1,p2]}]}
    """)
    print o.states[0].name
    print o.states[1].name
    print o.connections[0].pim
    sys.exit(0)

Gets:

p1
p2
['p1', 'p2']

And never try the yaml.load() in module's root block, always use if __name__ == '__main__' or call it within a function that you ensure it will be called once.

please note that yaml statement:

!!python/object:myyaml.State { name: p1 },

At this point yaml tries to import the myyaml.py again, in another context, and the all codes in the module's root will be executed, if you put yaml.load or anything like this in module's root, you may meet an infinite loop, or an unexpected result .

OTHER TIPS

To complement pylover's answer: if at some point you need more control on the serialization/deserialization process, try yamlable. I wrote this package for some of our production code, so as to gain a bit more control on the yaml-to-object binding.

In your example:

import yaml
import sys
from yamlable import YamlAble, yaml_info


@yaml_info(yaml_tag_ns='myyaml')
class StateMachine(YamlAble):

    def __init__(self, states, connections):
        self.states = states
        self.connections = connections

    # def to_yaml_dict(self):
    #     return vars(self)
    #
    # @classmethod
    # def from_yaml_dict(cls, dct, yaml_tag):
    #     return StateMachine(**dct)


@yaml_info(yaml_tag_ns='myyaml')
class State(YamlAble):

    def __init__(self, name):
        self.name = name

    # def to_yaml_dict(self):
    #     return vars(self)
    #
    # @classmethod
    # def from_yaml_dict(cls, dct, yaml_tag):
    #     return State(**dct)


@yaml_info(yaml_tag_ns='myyaml')
class Connection(YamlAble):

    def __init__(self, pim):
        self.pim = pim

    # def to_yaml_dict(self):
    #     return vars(self)
    #
    # @classmethod
    # def from_yaml_dict(cls, dct, yaml_tag):
    #     return Connection(**dct)


if __name__ == '__main__':
    o = yaml.safe_load("""
    !yamlable/myyaml.StateMachine {
         states: [
           !yamlable/myyaml.State { name: p1 },
           !yamlable/myyaml.State { name: p2 },
           !yamlable/myyaml.State { name: p3 },],
         connections:
          [       !yamlable/myyaml.Connection { 'pim' : [p1,p2]}]}
    """)
    print(o.states[0].name)
    print(o.states[1].name)
    print(o.connections[0].pim)

    print(yaml.safe_dump(o))

    # Note: these also work
    # print(o.loads_yaml(""" ... """))
    # print(o.dumps_yaml())

    sys.exit(0)

If you need to change the default behaviour, for example to only dump some of the fields, or to change their structure for dumping, or to perform some custom instance creation at loading, uncomment the appropriate methods.

For more details, see yamlable documentation

Licensed under: CC-BY-SA with attribution
Not affiliated with StackOverflow
scroll top