gmggroup / omf-python

Python library for working with OMF files
MIT License
79 stars 20 forks source link

Fail to deserialize "org.omf.v2.colormap.discrete" for numeric attribute colormap. #118

Open dbrookes96 opened 2 years ago

dbrookes96 commented 2 years ago

If I create an omf project with the following code.

def generate_test_project(): # pylint: disable=missing-function-docstring
    proj = omf.Project(
      name = "omf Test Project",
      description = "Randomly generated test project for omf."
  )

  ps = omf.PointSet(
      name = "Test Point Set",
      vertices = [[0.0, 0.0, 0.0], [1.0, 1.0, 1.0], [2.0, 2.0, 2.0], [3.0, 3.0, 3.0], 
      [4.0, 4.0, 4.0]])
  na = omf.NumericAttribute(
      name = "Test Numeric Attribute",
      array = [24, 32, 80, 150, 240],
      location = "vertices",
      colormap = omf.DiscreteColormap(
          name = "Test Discrete Colour Map",
          end_points = [ 0.0, 100.0, 300.0 ],
          end_inclusive = [ False, False, False ],
          colors = [ [ 255, 0, 0 ], [ 255, 215, 0 ], [ 255, 255, 0 ], [ 0, 255, 0 ] ]
      ))

  ps.attributes = [na]

  proj.elements = [ps]

  assert proj.validate()
  return proj

After saving it to a file and loading it back again we fail with the following error.

Traceback (most recent call last):
  File "C:\Users\dbrookes\Desktop\omf\load_generate_test_file.py", line 13, in <module>
    main()
  File "C:\Users\dbrookes\Desktop\omf\load_generate_test_file.py", line 10, in main
    project = omf.load("C:\\Users\\dbrookes\\Desktop\\omf\\generate_test_file.omf")
  File "c:\omf\omf\fileio.py", line 105, in load
    project = Project.deserialize(
  File "c:\omf\omf\base.py", line 33, in deserialize
    return super().deserialize(value, trusted, strict, assert_valid, **kwargs)
  File "C:\Users\dbrookes\AppData\Local\Programs\Python\Python310\lib\site-packages\properties\base\base.py", line 576, in deserialize
    newstate[key] = output_cls._props[key].deserialize(val, **kwargs)
  File "C:\Users\dbrookes\AppData\Local\Programs\Python\Python310\lib\site-packages\properties\base\containers.py", line 325, in deserialize
    output_list = [self.prop.deserialize(val, **kwargs)
  File "C:\Users\dbrookes\AppData\Local\Programs\Python\Python310\lib\site-packages\properties\base\containers.py", line 325, in <listcomp>
    output_list = [self.prop.deserialize(val, **kwargs)
  File "C:\Users\dbrookes\AppData\Local\Programs\Python\Python310\lib\site-packages\properties\base\instance.py", line 154, in deserialize
    return self.instance_class.deserialize(value, **kwargs)
  File "c:\omf\omf\base.py", line 33, in deserialize
    return super().deserialize(value, trusted, strict, assert_valid, **kwargs)
  File "C:\Users\dbrookes\AppData\Local\Programs\Python\Python310\lib\site-packages\properties\base\base.py", line 576, in deserialize
    newstate[key] = output_cls._props[key].deserialize(val, **kwargs)
  File "C:\Users\dbrookes\AppData\Local\Programs\Python\Python310\lib\site-packages\properties\base\containers.py", line 325, in deserialize
    output_list = [self.prop.deserialize(val, **kwargs)
  File "C:\Users\dbrookes\AppData\Local\Programs\Python\Python310\lib\site-packages\properties\base\containers.py", line 325, in <listcomp>
    output_list = [self.prop.deserialize(val, **kwargs)
  File "C:\Users\dbrookes\AppData\Local\Programs\Python\Python310\lib\site-packages\properties\base\instance.py", line 154, in deserialize
    return self.instance_class.deserialize(value, **kwargs)
  File "c:\omf\omf\base.py", line 33, in deserialize
    return super().deserialize(value, trusted, strict, assert_valid, **kwargs)
  File "C:\Users\dbrookes\AppData\Local\Programs\Python\Python310\lib\site-packages\properties\base\base.py", line 580, in deserialize
    instance = output_cls(**mutable)
  File "C:\Users\dbrookes\AppData\Local\Programs\Python\Python310\lib\site-packages\properties\base\base.py", line 280, in __call__
    obj.__init__(*args, **kwargs)
  File "C:\Users\dbrookes\AppData\Local\Programs\Python\Python310\lib\site-packages\properties\base\base.py", line 335, in __init__
    raise utils.ValidationError(
properties.utils.ValidationError: Initialization failed:
- The Union property 'colormap' of a NumericAttribute instance must be an instance of ContinuousColormap or an instance of DiscreteColormap. An invalid value of {'name': 'Test Discrete Colour Map', 'description'  ...  , 255, 0], [0, 255, 0]
], '__class__': 'BaseModel'} <class 'dict'> was specified. Possible explanation:
    - The Instance property 'colormap' of a NumericAttribute instance must be an instance of ContinuousColormap. An invalid value of {'name': 'Test Discrete Colour Map', 'description'  ...  , 255, 0], [0, 255, 0]], '__class__': 'BaseModel'}
 <class 'dict'> was specified.
    - The Instance property 'colormap' of a NumericAttribute instance must be an instance of DiscreteColormap. An invalid value of {'name': 'Test Discrete Colour Map', 'description'  ...  , 255, 0], [0, 255, 0]], '__class__': 'BaseModel'} <
class 'dict'> was specified.

The same thing, but with a continuous color map instead, works as expected.

dbrookes96 commented 2 years ago

It looks like providing the __class__ attribute in the object works around this issue. The issue is also present in the Composite class that uses a properties.Union. I'm not enough of a python wizard to figure out why that is...