OpenJobDescription / openjd-model-for-python

Provides a Python implementation of the data model for Open Job Description's template schemas.
https://github.com/OpenJobDescription/openjd-specifications/wiki
Apache License 2.0
12 stars 7 forks source link

Bug: YAML Generation #108

Open pta200 opened 4 months ago

pta200 commented 4 months ago

Expected Behaviour

Following the example from the readme I would expect to be able to do generate YAML as easily as JSON. obj = model_to_object(model=job_template) print(yaml.safe_dump(obj))

Current Behaviour

Current behavior is that pyyaml throws the following exception processing the job name.

> Traceback (most recent call last):
  File "/mnt/home/paolo.audiberti/project-src/farmtests/farmtests/model_gen_job.py", line 92, in <module>
    print(yaml.safe_dump(model_to_object(model=job_template)))
  File "/mnt/home/paolo.audiberti/project-src/farmtests/.venv/farmtests-cLCMXaGk-py3.9/lib64/python3.9/site-packages/yaml/__init__.py", line 269, in safe_dump
    return dump_all([data], stream, Dumper=SafeDumper, **kwds)
  File "/mnt/home/paolo.audiberti/project-src/farmtests/.venv/farmtests-cLCMXaGk-py3.9/lib64/python3.9/site-packages/yaml/__init__.py", line 241, in dump_all
    dumper.represent(data)
  File "/mnt/home/paolo.audiberti/project-src/farmtests/.venv/farmtests-cLCMXaGk-py3.9/lib64/python3.9/site-packages/yaml/representer.py", line 27, in represent
    node = self.represent_data(data)
  File "/mnt/home/paolo.audiberti/project-src/farmtests/.venv/farmtests-cLCMXaGk-py3.9/lib64/python3.9/site-packages/yaml/representer.py", line 48, in represent_data
    node = self.yaml_representers[data_types[0]](self, data)
  File "/mnt/home/paolo.audiberti/project-src/farmtests/.venv/farmtests-cLCMXaGk-py3.9/lib64/python3.9/site-packages/yaml/representer.py", line 207, in represent_dict
    return self.represent_mapping('tag:yaml.org,2002:map', data)
  File "/mnt/home/paolo.audiberti/project-src/farmtests/.venv/farmtests-cLCMXaGk-py3.9/lib64/python3.9/site-packages/yaml/representer.py", line 118, in represent_mapping
    node_value = self.represent_data(item_value)
  File "/mnt/home/paolo.audiberti/project-src/farmtests/.venv/farmtests-cLCMXaGk-py3.9/lib64/python3.9/site-packages/yaml/representer.py", line 58, in represent_data
    node = self.yaml_representers[None](self, data)
  File "/mnt/home/paolo.audiberti/project-src/farmtests/.venv/farmtests-cLCMXaGk-py3.9/lib64/python3.9/site-packages/yaml/representer.py", line 231, in represent_undefined
    raise RepresenterError("cannot represent an object", data)
yaml.representer.RepresenterError: ('cannot represent an object', 'DemoJob')

Reproduction Steps

Run the included code snipet

Code Snippet

import json
import yaml
import uuid
from pathlib import Path

from openjd.model import (
    decode_job_template,
    model_to_object,
    DocumentType,

)
from openjd.model.v2023_09 import *

  job_template_path = Path("/test-job/template.yaml")
  job_template = JobTemplate(
      specificationVersion="jobtemplate-2023-09",
      name="DemoJob",
      parameterDefinitions=[
          JobPathParameterDefinition(
              name="InFile",
              type=JobParameterType.PATH,
              objectType="FILE",
              dataFlow="IN",
              ),
          JobPathParameterDefinition(
              name="OutFile",
              type=JobParameterType.PATH,
              objectType="FILE",
              dataFlow="OUT",
              )
          ],
      steps=[
          StepTemplate(
              name="RenderStep",
              script=StepScript(
                  actions=StepActions(
                      onRun=Action(
                          command="{{Task.File.Run}}"
                      )
                  ),
                  embeddedFiles=[
                      EmbeddedFileText(
                      name="Run",
                      type=EmbeddedFileTypes.TEXT,
                      data="#!/usr/bin/env bash \n echo 'openjd_status: START JOB' ;  sha256sum '{{Param.InFile}}' > '{{Param.OutFile}}'  ",
                      runnable=True
                      )
                  ]
              )
          )
      ]
  )

  print(yaml.safe_dump(model_to_object(model=job_template)))
ddneilson commented 4 months ago

Thanks for the report!

From the stack trace (node = self.yaml_representers[None](self, data)), it looks like we're leaving some None values in the object and the yaml encoder doesn't like that.

It may be related to this code. We already delete None valued keys, but don't do so if the value is an array element. So, I'd wager that we have an array containing None values in the data model, for some reason.

Assignee will want to dig into what exactly the resulting model is and figure out where the None values are to narrow in on a fix.