Search code examples
pythonyamlpyyamlruamel.yaml

create yaml python from dict add 2 lines at top of dict


I am working to create a yaml config file that has some flexibility. Ideally i need to create a blank config file with the fields.

The yaml configuration file needs to have two mandatory lines at the very top.

These are the mandatory attributes that need to be at the top of the config file.

Component: "groups"
groups: 

however i need to add the flexibility to allow a user to specify how many groups they would like to add. They may need 10 they may need 3. The script will build out the following:

  group1:
    name: "Developers"
    description: "Developers"
    location: "US-East"
    code: "duse"
  group2:
    name: "QA"
    description: "QA"
    location: "US-West"
    code: "qawest"   
  group3:
    name: "Admin"
    description: "Admin"
    location: "US-Central"
    code: "acent" 

The final config file will look like the following:

Component: "groups"
groups: 
  group1:
    name: "Developers"
    description: "Developers"
    location: "US-East"
    code: "duse"
  group2:
    name: "QA"
    description: "QA"
    location: "US-West"
    code: "qawest"   
  group3:
    name: "Admin"
    description: "Admin"
    location: "US-Central"
    code: "acent" 

The challenge is i have the following script:

total_groups = int(input(f'How many total groups? \n'))
d = []
for i in range(0, total_groups):
    test = str(i)
    dtest = {
        'groups': {
            'group'+test: {
                'name': '',
                'desc': '',
                'loc': '',
                'code': ''
            }
        }
    }
    d.append(dtest)


print(yaml.dump(d, default_flow_style=False))

The output of the script is the following:

How many total groups? 
3
- groups:
    group0:
      code: ''
      desc: ''
      loc: ''
      name: ''
- groups:
    group1:
      code: ''
      desc: ''
      loc: ''
      name: ''
- groups:
    group2:
      code: ''
      desc: ''
      loc: ''
      name: ''

I need to one remove the -groups: and only have that at the top of the file. I also need to figure out how to add the component at the top of the file as well. I found the following and can't seem to figure out how to make this work.

>>> d = {}
>>> for i in range(0,5):
...      d.setdefault('result', [])
...      d['result'].append(i)

Any assistance on how to remove the -groups: and make that a fixture at the top of the script along with the component: 'groups' would be greatly appreciated.


Solution

  • The key (pun intended) is to look at the structure you want in your YAML file, and represent that in Python. Rather than thinking about this line and that line, think about what the structure represents. For the desired output:

    Component: "groups"
    groups: 
      group1:
        name: "Developers"
        description: "Developers"
        location: "US-East"
        code: "duse"
      group2:
        name: "QA"
        description: "QA"
        location: "US-West"
        code: "qawest"   
      group3:
        name: "Admin"
        description: "Admin"
        location: "US-Central"
        code: "acent" 
    

    The data is a dictionary with two keys: Component, whose value is the string groups, and groups, whose value is another dictionary. In other words, in Python, it would look like this:

    {
        'Component': 'groups',
        'groups': {
            'group1': {
                'name': 'Developers',
                'description': 'Developers',
                'location': 'US-East',
                'code': 'duse',
            },
            'group2': {
                'name': 'QA',
                'description': 'QA',
                'location': 'US-West',
                'code': 'qawest',
            },
            'group3': {
                'name': 'Admin',
                'description': 'Admin',
                'location': 'US-Central',
                'code': 'acent' ,
            },
        },
    }
    

    You could build this like so:

    import sys
    
    from ruamel.yaml import YAML
    
    # Initialize the YAML parser/dumper.
    yaml = YAML()
    yaml.default_flow_style = False
    
    total_groups = int(input('How many total groups? \n'))
    
    # Initialize a new dictionary to hold the groups.
    groups = {}
    
    for i in range(1, total_groups + 1):
        # Initialize a new dictionary for each group.
        group = {}
    
        print(f'Data for group #{i}')
        # Get the data for each group.
        for field in ['name', 'description', 'location', 'code']:
            group[field] = input(f'\tEnter {field}: ')
        
        # Add the new group's dictionary to the groups dictionary.
        groups[f'group{i}'] = group
    
    # Create the top-level dictionary and insert groups.
    data = {'Component': 'groups', 'groups': groups}
    
    # Dump to stdout (which prints it).
    yaml.dump(data, sys.stdout)
    

    (I've used ruamel.yaml, which was one of your tags, even though your code used a different format—PyYAML, I imagine? Same difference either way, really.)

    However, I'm not sure this data structure is ideal; group1, group2, etc. are essentially meaningless keys. I would recommend instead either using the groups' names as the keys, or else eschewing keys entirely and using a list of groups instead of a dictionary. (Note that using names as keys would require all names to be unique.)