diff --git a/orsopy/fileio/base.py b/orsopy/fileio/base.py index edfc28c6..e6d02371 100644 --- a/orsopy/fileio/base.py +++ b/orsopy/fileio/base.py @@ -244,7 +244,14 @@ def _resolve_type(hint: type, item: Any) -> Any: else: return hbase([Header._resolve_type(t0, item)]) elif hbase is dict: - value_type = get_args(hint)[1] + try: + value_type = get_args(hint)[1] + except IndexError: + warnings.warn( + "The evaluation of type hints requires key/value definition for Dict, " + "if you want to use unspecified dictionaries use dict instead of Dict." + ) + raise try: for key, value in item.items(): # resolve the type of any value in the dictionary diff --git a/orsopy/fileio/data_source.py b/orsopy/fileio/data_source.py index 1ed63642..7575edc2 100644 --- a/orsopy/fileio/data_source.py +++ b/orsopy/fileio/data_source.py @@ -8,7 +8,7 @@ import yaml -from .base import File, Header, Person, Value, ValueRange, ValueVector, orsodataclass +from .base import ComplexValue, File, Header, Person, Value, ValueRange, ValueVector, orsodataclass # typing stuff introduced in python 3.8 try: @@ -66,7 +66,7 @@ class Sample(Header): composition: Optional[str] = None description: Optional[str] = None environment: Optional[List[str]] = None - sample_parameters: Optional[Dict] = field( + sample_parameters: Optional[Dict[str, Union[Value, ValueRange, ValueVector, ComplexValue]]] = field( default=None, metadata={"description": "Using keys for parameters and Value* objects for values."} ) diff --git a/orsopy/fileio/schema/refl_header.schema.json b/orsopy/fileio/schema/refl_header.schema.json index baf2b2d5..b1115a67 100644 --- a/orsopy/fileio/schema/refl_header.schema.json +++ b/orsopy/fileio/schema/refl_header.schema.json @@ -168,47 +168,54 @@ "probe" ] }, - "Sample": { - "title": "Sample", + "Value": { + "title": "Value", "type": "object", "properties": { - "name": { + "magnitude": { "type": [ - "string", + "number", "null" ] }, - "category": { + "unit": { + "description": "SI unit string", "type": [ "string", "null" ] }, - "composition": { + "comment": { "type": [ "string", "null" ] - }, - "description": { + } + }, + "required": [ + "magnitude" + ] + }, + "ValueRange": { + "title": "ValueRange", + "type": "object", + "properties": { + "min": { "type": [ - "string", + "number", "null" ] }, - "environment": { - "items": { - "type": "string" - }, + "max": { "type": [ - "array", + "number", "null" ] }, - "sample_parameters": { - "description": "Using keys for parameters and Value* objects for values.", + "unit": { + "description": "SI unit string", "type": [ - "object", + "string", "null" ] }, @@ -220,14 +227,27 @@ } }, "required": [ - "name" + "min", + "max" ] }, - "Value": { - "title": "Value", + "ValueVector": { + "title": "ValueVector", "type": "object", "properties": { - "magnitude": { + "x": { + "type": [ + "number", + "null" + ] + }, + "y": { + "type": [ + "number", + "null" + ] + }, + "z": { "type": [ "number", "null" @@ -248,23 +268,45 @@ } }, "required": [ - "magnitude" + "x", + "y", + "z" ] }, - "ValueRange": { - "title": "ValueRange", + "ComplexValue": { + "title": "ComplexValue", "type": "object", "properties": { - "min": { - "type": [ - "number", - "null" + "real": { + "anyOf": [ + { + "type": "number" + }, + { + "type": "array", + "items": { + "type": "number" + } + }, + { + "type": "null" + } ] }, - "max": { - "type": [ - "number", - "null" + "imag": { + "anyOf": [ + { + "type": "number" + }, + { + "type": "array", + "items": { + "type": "number" + } + }, + { + "type": "null" + } ] }, "unit": { @@ -282,55 +324,69 @@ } }, "required": [ - "min", - "max" + "real" ] }, - "Polarization": { - "title": "Polarization", - "description": "The first symbol indicates the magnetisation direction of the incident\nbeam. An optional second symbol indicates the direction of the scattered\nbeam, if a spin analyser is present.", - "enum": [ - "unpolarized", - "po", - "mo", - "op", - "om", - "mm", - "mp", - "pm", - "pp" - ], - "type": "string" - }, - "ValueVector": { - "title": "ValueVector", + "Sample": { + "title": "Sample", "type": "object", "properties": { - "x": { + "name": { "type": [ - "number", + "string", "null" ] }, - "y": { + "category": { "type": [ - "number", + "string", "null" ] }, - "z": { + "composition": { "type": [ - "number", + "string", "null" ] }, - "unit": { - "description": "SI unit string", + "description": { "type": [ "string", "null" ] }, + "environment": { + "items": { + "type": "string" + }, + "type": [ + "array", + "null" + ] + }, + "sample_parameters": { + "description": "Using keys for parameters and Value* objects for values.", + "additionalProperties": { + "anyOf": [ + { + "$ref": "#/definitions/Value" + }, + { + "$ref": "#/definitions/ValueRange" + }, + { + "$ref": "#/definitions/ValueVector" + }, + { + "$ref": "#/definitions/ComplexValue" + } + ] + }, + "type": [ + "object", + "null" + ] + }, "comment": { "type": [ "string", @@ -339,11 +395,25 @@ } }, "required": [ - "x", - "y", - "z" + "name" ] }, + "Polarization": { + "title": "Polarization", + "description": "The first symbol indicates the magnetisation direction of the incident\nbeam. An optional second symbol indicates the direction of the scattered\nbeam, if a spin analyser is present.", + "enum": [ + "unpolarized", + "po", + "mo", + "op", + "om", + "mm", + "mp", + "pm", + "pp" + ], + "type": "string" + }, "InstrumentSettings": { "title": "InstrumentSettings", "type": "object", @@ -760,6 +830,8 @@ "enum": [ "1/angstrom", "1/nm", + "1", + "1/s", null ] }, @@ -802,6 +874,8 @@ "enum": [ "1/angstrom", "1/nm", + "1", + "1/s", null ] }, diff --git a/orsopy/fileio/schema/refl_header.schema.yaml b/orsopy/fileio/schema/refl_header.schema.yaml index d6682060..69959147 100644 --- a/orsopy/fileio/schema/refl_header.schema.yaml +++ b/orsopy/fileio/schema/refl_header.schema.yaml @@ -25,6 +25,35 @@ definitions: - name title: Column type: object + ComplexValue: + properties: + comment: + type: + - string + - 'null' + imag: + anyOf: + - type: number + - items: + type: number + type: array + - type: 'null' + real: + anyOf: + - type: number + - items: + type: number + type: array + - type: 'null' + unit: + description: SI unit string + type: + - string + - 'null' + required: + - real + title: ComplexValue + type: object DataSource: properties: comment: @@ -289,6 +318,8 @@ definitions: enum: - 1/angstrom - 1/nm + - '1' + - 1/s - null required: - name @@ -312,6 +343,8 @@ definitions: enum: - 1/angstrom - 1/nm + - '1' + - 1/s - null required: - name @@ -394,6 +427,12 @@ definitions: - string - 'null' sample_parameters: + additionalProperties: + anyOf: + - $ref: '#/definitions/Value' + - $ref: '#/definitions/ValueRange' + - $ref: '#/definitions/ValueVector' + - $ref: '#/definitions/ComplexValue' description: Using keys for parameters and Value* objects for values. type: - object diff --git a/tests/test_fileio/test_data_source.py b/tests/test_fileio/test_data_source.py index 43fb6a93..1a956779 100644 --- a/tests/test_fileio/test_data_source.py +++ b/tests/test_fileio/test_data_source.py @@ -115,12 +115,14 @@ def test_creation_optionals(self): composition="Si | SiO2(20 A) | Fe(200 A) | air(beam side)", description="The sample is without flaws", environment=["Temperature cell"], + sample_parameters={}, ) assert value.name == "A Perfect Sample" assert value.category == "solid/gas" assert value.composition == "Si | SiO2(20 A) | " + "Fe(200 A) | air(beam side)" assert value.description == "The sample is without flaws" assert value.environment == ["Temperature cell"] + assert value.sample_parameters == {} def test_to_yaml_optionals(self): """ diff --git a/tools/header_schema.py b/tools/header_schema.py index 683f5a81..7df7cc98 100644 --- a/tools/header_schema.py +++ b/tools/header_schema.py @@ -46,7 +46,7 @@ def schema_extra(schema: Dict[str, Any]) -> None: "type": "object", "properties": { "name": {"enum": [""]}, - "unit": {"enum": ["1/angstrom", "1/nm", None]}, + "unit": {"enum": ["1/angstrom", "1/nm", "1", "1/s", None]}, "dimension": {"dimension": "dimension of column", "anyOf": [{"type": "string"}, {"type": "null"}]}, "comment": {"anyOf": [{"type": "string"}, {"type": "null"}]}, },