Commit 21a14af3 authored by Markus Scheidgen's avatar Markus Scheidgen
Browse files

Started to add type specific serialization support.

parent fb20510a
...@@ -96,4 +96,4 @@ if __name__ == '__main__': ...@@ -96,4 +96,4 @@ if __name__ == '__main__':
# To serialize the data: # To serialize the data:
print(run.m_to_json(indent=2)) print(run.m_to_json(indent=2))
# print(m_package.m_to_json(indent=2)) # type: ignore, pylint: disable=undefined-variable print(m_package.m_to_json(indent=2)) # type: ignore, pylint: disable=undefined-variable
...@@ -168,6 +168,7 @@ class DataType: ...@@ -168,6 +168,7 @@ class DataType:
However, in some occasions you need to add custom data types. However, in some occasions you need to add custom data types.
""" """
def type_check(self, section, value): def type_check(self, section, value):
""" Checks the given value before it is set to the given section. Can modify the value. """
return value return value
def to_json_serializable(self, section, value): def to_json_serializable(self, section, value):
...@@ -209,7 +210,22 @@ class Reference(DataType): ...@@ -209,7 +210,22 @@ class Reference(DataType):
self.section = section self.section = section
# TODO class Unit(DataType) class Unit(DataType):
def type_check(self, section, value):
if isinstance(value, str):
value = units.parse_units(value)
elif not isinstance(value, _Unit):
raise TypeError('Units must be given as str or pint Unit instances.')
return value
def to_json_serializable(self, section, value):
return value.__str__()
def from_json_serializable(self, section, value):
return units.parse_units(value)
# TODO class Datetime(DataType) # TODO class Datetime(DataType)
...@@ -606,6 +622,7 @@ class MSection(metaclass=MObjectMeta): ...@@ -606,6 +622,7 @@ class MSection(metaclass=MObjectMeta):
setattr(self, name, value) setattr(self, name, value)
def m_follows(self, definition: 'Section') -> bool: def m_follows(self, definition: 'Section') -> bool:
""" Determines if this section's definition is or is derived from the given definition. """
return self.m_def == definition or self.m_def in definition.all_base_sections return self.m_def == definition or self.m_def in definition.all_base_sections
def m_to_dict(self) -> Dict[str, Any]: def m_to_dict(self) -> Dict[str, Any]:
...@@ -627,29 +644,43 @@ class MSection(metaclass=MObjectMeta): ...@@ -627,29 +644,43 @@ class MSection(metaclass=MObjectMeta):
for name, quantity in self.m_def.all_quantities.items(): for name, quantity in self.m_def.all_quantities.items():
if name in self.m_data: if name in self.m_data:
to_json_serializable = str
if isinstance(quantity.type, DataType):
to_json_serializable = lambda v: quantity.type.to_json_serializable(self, v)
elif isinstance(quantity.type, Section):
# TODO
to_json_serializable = str
else:
# TODO
pass
value = getattr(self, name) value = getattr(self, name)
if hasattr(value, 'tolist'): if hasattr(value, 'tolist'):
value = value.tolist() serializable_value = value.tolist()
# TODO else:
if isinstance(quantity.type, Section): if len(quantity.shape) == 0:
value = str(value) serializable_value = to_json_serializable(value)
# TODO elif len(quantity.shape) == 1:
if isinstance(value, type): serializable_value = [to_json_serializable(i) for i in value]
value = str(value) else:
# TODO raise NotImplementedError('Higher shapes (%s) not supported: %s' % (quantity.shape, quantity))
if isinstance(value, np.dtype):
value = str(value) yield name, serializable_value
# TODO
if isinstance(value, _Unit):
value = str(value)
yield name, value
return {key: value for key, value in items()} return {key: value for key, value in items()}
@classmethod @classmethod
def m_from_dict(cls: Type[MSectionBound], dct: Dict[str, Any]) -> MSectionBound: def m_from_dict(cls: Type[MSectionBound], dct: Dict[str, Any]) -> MSectionBound:
""" Creates a section from the given data dictionary.
This is the 'oposite' of :func:`m_to_dict`. It takes a deserialized dict, e.g
loaded from JSON, and turns it into a proper section, i.e. instance of the given
section class.
"""
section_def = cls.m_def section_def = cls.m_def
# remove m_def and m_parent_index, they set themselves automatically # remove m_def and m_parent_index, they set themselves automatically
...@@ -1096,7 +1127,7 @@ Quantity.type = Quantity( ...@@ -1096,7 +1127,7 @@ Quantity.type = Quantity(
In the NOMAD CoE meta-info this was basically the ``dTypeStr``. In the NOMAD CoE meta-info this was basically the ``dTypeStr``.
''') ''')
Quantity.shape = Quantity( Quantity.shape = Quantity(
type=Dimension(), shape=['0..*'], name='shape', description=''' type=Dimension(), shape=['0..*'], name='shape', default=[], description='''
The shape of the quantity that defines its dimensionality. The shape of the quantity that defines its dimensionality.
A shape is a list, where each item defines a dimension. Each dimension can be: A shape is a list, where each item defines a dimension. Each dimension can be:
...@@ -1108,7 +1139,7 @@ Quantity.shape = Quantity( ...@@ -1108,7 +1139,7 @@ Quantity.shape = Quantity(
and an upper bound (int or ``*`` denoting arbitrary large), e.g. ``'0..*'``, ``'1..3'`` and an upper bound (int or ``*`` denoting arbitrary large), e.g. ``'0..*'``, ``'1..3'``
''') ''')
Quantity.unit = Quantity( Quantity.unit = Quantity(
type=_Unit, description=''' type=Unit(), description='''
The optional physics unit for this quantity. The optional physics unit for this quantity.
Units are given in `pint` units. Pint is a Python package that defines units and Units are given in `pint` units. Pint is a Python package that defines units and
......
...@@ -15,7 +15,7 @@ ...@@ -15,7 +15,7 @@
import pytest import pytest
import numpy as np import numpy as np
from nomad.metainfo.metainfo import MSection, MCategory, Section, Quantity, Definition, Category, SubSection from nomad.metainfo.metainfo import MSection, MCategory, Section, Quantity, Definition, Category
from nomad.metainfo.example import Run, System, SystemHash, Parsing, m_package as example_package from nomad.metainfo.example import Run, System, SystemHash, Parsing, m_package as example_package
...@@ -150,6 +150,9 @@ class TestM2: ...@@ -150,6 +150,9 @@ class TestM2:
assert 'name' in Section.m_def.all_quantities assert 'name' in Section.m_def.all_quantities
assert 'name' in Quantity.m_def.all_quantities assert 'name' in Quantity.m_def.all_quantities
def test_unit(self):
assert System.lattice_vectors.unit is not None
class TestM1: class TestM1:
""" Test for meta-info instances. """ """ Test for meta-info instances. """
......
Supports Markdown
0% or .
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment