Repository: bluesky/event-model (language: python)

Issue:
Add NotRequired things to the DataKey schema
As per https://github.com/bluesky/ophyd-async/pull/137#discussion_r1624442103

Also removing the `Optional` from "units" and "precision"

https://github.com/bluesky/event-model/blob/fc6e30eae896073eda4013f7c4af3362fead61c1/event_model/documents/event_descriptor.py#L59


Discussion / hints:
Also `choices`

Fix patch:
diff --git a/event_model/__init__.py b/event_model/__init__.py
index 038bf59e0..78788561d 100644
--- a/event_model/__init__.py
+++ b/event_model/__init__.py
@@ -34,6 +34,7 @@
 import numpy
 from typing_extensions import Literal
 
+from .documents import Dtype
 from .documents.datum import Datum
 from .documents.datum_page import DatumPage
 from .documents.event import Event
@@ -61,6 +62,7 @@
 
 
 __all__ = [
+    "Dtype",
     "DocumentNames",
     "schemas",
     "schema_validators",
diff --git a/event_model/documents/__init__.py b/event_model/documents/__init__.py
index f9796a994..33a2faefd 100644
--- a/event_model/documents/__init__.py
+++ b/event_model/documents/__init__.py
@@ -4,7 +4,12 @@
 from event_model.documents.datum import Datum
 from event_model.documents.datum_page import DatumPage
 from event_model.documents.event import Event
-from event_model.documents.event_descriptor import EventDescriptor
+from event_model.documents.event_descriptor import (
+    Dtype,
+    EventDescriptor,
+    Limits,
+    LimitsRange,
+)
 from event_model.documents.event_page import EventPage
 from event_model.documents.resource import Resource
 from event_model.documents.run_start import RunStart
@@ -42,9 +47,12 @@
 __all__ = [
     "Datum",
     "DatumPage",
+    "Dtype",
     "Event",
     "EventDescriptor",
     "EventPage",
+    "Limits",
+    "LimitsRange",
     "Resource",
     "RunStart",
     "RunStop",
diff --git a/event_model/documents/event_descriptor.py b/event_model/documents/event_descriptor.py
index 3fa799a9b..26f6d447c 100644
--- a/event_model/documents/event_descriptor.py
+++ b/event_model/documents/event_descriptor.py
@@ -7,9 +7,30 @@
 Dtype = Literal["string", "number", "array", "boolean", "integer"]
 
 
+class LimitsRange(TypedDict):
+    low: Optional[float]
+    high: Optional[float]
+
+
+class Limits(TypedDict):
+    """
+    Epics limits:
+    see 3.4.1 https://epics.anl.gov/base/R3-14/12-docs/AppDevGuide/node4.html
+    """
+
+    control: NotRequired[Annotated[LimitsRange, Field(description="Control limits.")]]
+    display: NotRequired[Annotated[LimitsRange, Field(description="Display limits.")]]
+    warning: NotRequired[Annotated[LimitsRange, Field(description="Warning limits.")]]
+    alarm: NotRequired[Annotated[LimitsRange, Field(description="Alarm limits.")]]
+
+
 class DataKey(TypedDict):
     """Describes the objects in the data property of Event documents"""
 
+    limits: NotRequired[Annotated[Limits, Field(description="Epics limits.")]]
+    choices: NotRequired[
+        Annotated[List[str], Field(description="Choices of enum value.")]
+    ]
     dims: NotRequired[
         Annotated[
             List[str],
@@ -41,7 +62,7 @@ class DataKey(TypedDict):
     ]
     precision: NotRequired[
         Annotated[
-            Optional[int],
+            int,
             Field(
                 description="Number of digits after decimal place if "
                 "a floating point number"
@@ -56,7 +77,7 @@ class DataKey(TypedDict):
         str, Field(description="The source (ex piece of hardware) of the data.")
     ]
     units: NotRequired[
-        Annotated[Optional[str], Field(description="Engineering units of the value")]
+        Annotated[str, Field(description="Engineering units of the value")]
     ]
 
 
diff --git a/event_model/schemas/datum_page.json b/event_model/schemas/datum_page.json
index a09dc59f4..7d34f9882 100644
--- a/event_model/schemas/datum_page.json
+++ b/event_model/schemas/datum_page.json
@@ -14,11 +14,7 @@
     "properties": {
         "datum_id": {
             "description": "Array unique identifiers for each Datum (akin to 'uid' for other Document types), typically formatted as '<resource>/<integer>'",
-            "allOf": [
-                {
-                    "$ref": "#/$defs/DataFrameForDatumPage"
-                }
-            ]
+            "$ref": "#/$defs/DataFrameForDatumPage"
         },
         "datum_kwargs": {
             "title": "Datum Kwargs",
diff --git a/event_model/schemas/event_descriptor.json b/event_model/schemas/event_descriptor.json
index 3549af5fb..e202806b1 100644
--- a/event_model/schemas/event_descriptor.json
+++ b/event_model/schemas/event_descriptor.json
@@ -32,6 +32,14 @@
             "description": "Describes the objects in the data property of Event documents",
             "type": "object",
             "properties": {
+                "choices": {
+                    "title": "Choices",
+                    "description": "Choices of enum value.",
+                    "type": "array",
+                    "items": {
+                        "type": "string"
+                    }
+                },
                 "dims": {
                     "title": "Dims",
                     "description": "The names for dimensions of the data. Null or empty list if scalar data",
@@ -58,6 +66,10 @@
                     "type": "string",
                     "pattern": "^[A-Z]+:?"
                 },
+                "limits": {
+                    "description": "Epics limits.",
+                    "$ref": "#/$defs/Limits"
+                },
                 "object_name": {
                     "title": "Object Name",
                     "description": "The name of the object this key was pulled from.",
@@ -66,14 +78,7 @@
                 "precision": {
                     "title": "Precision",
                     "description": "Number of digits after decimal place if a floating point number",
-                    "anyOf": [
-                        {
-                            "type": "integer"
-                        },
-                        {
-                            "type": "null"
-                        }
-                    ]
+                    "type": "integer"
                 },
                 "shape": {
                     "title": "Shape",
@@ -91,9 +96,58 @@
                 "units": {
                     "title": "Units",
                     "description": "Engineering units of the value",
+                    "type": "string"
+                }
+            },
+            "required": [
+                "dtype",
+                "shape",
+                "source"
+            ]
+        },
+        "Limits": {
+            "title": "Limits",
+            "description": "Epics limits:\nsee 3.4.1 https://epics.anl.gov/base/R3-14/12-docs/AppDevGuide/node4.html",
+            "type": "object",
+            "properties": {
+                "alarm": {
+                    "description": "Alarm limits.",
+                    "$ref": "#/$defs/LimitsRange"
+                },
+                "control": {
+                    "description": "Control limits.",
+                    "$ref": "#/$defs/LimitsRange"
+                },
+                "display": {
+                    "description": "Display limits.",
+                    "$ref": "#/$defs/LimitsRange"
+                },
+                "warning": {
+                    "description": "Warning limits.",
+                    "$ref": "#/$defs/LimitsRange"
+                }
+            }
+        },
+        "LimitsRange": {
+            "title": "LimitsRange",
+            "type": "object",
+            "properties": {
+                "high": {
+                    "title": "High",
                     "anyOf": [
                         {
-                            "type": "string"
+                            "type": "number"
+                        },
+                        {
+                            "type": "null"
+                        }
+                    ]
+                },
+                "low": {
+                    "title": "Low",
+                    "anyOf": [
+                        {
+                            "type": "number"
                         },
                         {
                             "type": "null"
@@ -102,9 +156,8 @@
                 }
             },
             "required": [
-                "dtype",
-                "shape",
-                "source"
+                "high",
+                "low"
             ]
         },
         "PerObjectHint": {
diff --git a/event_model/schemas/event_page.json b/event_model/schemas/event_page.json
index 55cf9c238..7c9874275 100644
--- a/event_model/schemas/event_page.json
+++ b/event_model/schemas/event_page.json
@@ -32,11 +32,7 @@
     "properties": {
         "data": {
             "description": "The actual measurement data",
-            "allOf": [
-                {
-                    "$ref": "#/$defs/DataFrameForEventPage"
-                }
-            ]
+            "$ref": "#/$defs/DataFrameForEventPage"
         },
         "descriptor": {
             "title": "Descriptor",
@@ -45,11 +41,7 @@
         },
         "filled": {
             "description": "Mapping each of the keys of externally-stored data to an array containing the boolean False, indicating that the data has not been loaded, or to foreign keys (moved here from 'data' when the data was loaded)",
-            "allOf": [
-                {
-                    "$ref": "#/$defs/DataFrameForFilled"
-                }
-            ]
+            "$ref": "#/$defs/DataFrameForFilled"
         },
         "seq_num": {
             "title": "Seq Num",
@@ -69,11 +61,7 @@
         },
         "timestamps": {
             "description": "The timestamps of the individual measurement data",
-            "allOf": [
-                {
-                    "$ref": "#/$defs/DataFrameForEventPage"
-                }
-            ]
+            "$ref": "#/$defs/DataFrameForEventPage"
         },
         "uid": {
             "title": "Uid",
diff --git a/event_model/schemas/run_start.json b/event_model/schemas/run_start.json
index e7c019f1c..852e44b14 100644
--- a/event_model/schemas/run_start.json
+++ b/event_model/schemas/run_start.json
@@ -72,11 +72,7 @@
                 "calculation": {
                     "title": "calculation properties",
                     "description": "required fields if type is calculated",
-                    "allOf": [
-                        {
-                            "$ref": "#/$defs/Calculation"
-                        }
-                    ]
+                    "$ref": "#/$defs/Calculation"
                 },
                 "config_device": {
                     "title": "Config Device",
@@ -289,11 +285,7 @@
         },
         "data_type": {
             "description": "",
-            "allOf": [
-                {
-                    "$ref": "#/$defs/DataType"
-                }
-            ]
+            "$ref": "#/$defs/DataType"
         },
         "group": {
             "title": "Group",
diff --git a/event_model/schemas/run_stop.json b/event_model/schemas/run_stop.json
index 89f518155..b0d644584 100644
--- a/event_model/schemas/run_stop.json
+++ b/event_model/schemas/run_stop.json
@@ -10,11 +10,7 @@
     "properties": {
         "data_type": {
             "description": "data_type",
-            "allOf": [
-                {
-                    "$ref": "#/$defs/DataType"
-                }
-            ]
+            "$ref": "#/$defs/DataType"
         },
         "exit_status": {
             "title": "Exit Status",
diff --git a/event_model/schemas/stream_datum.json b/event_model/schemas/stream_datum.json
index d5217f39f..75b53ed76 100644
--- a/event_model/schemas/stream_datum.json
+++ b/event_model/schemas/stream_datum.json
@@ -33,19 +33,11 @@
         },
         "indices": {
             "description": "A slice object passed to the StreamResource handler so it can hand back data and timestamps",
-            "allOf": [
-                {
-                    "$ref": "#/$defs/StreamRange"
-                }
-            ]
+            "$ref": "#/$defs/StreamRange"
         },
         "seq_nums": {
             "description": "A slice object showing the Event numbers the resource corresponds to",
-            "allOf": [
-                {
-                    "$ref": "#/$defs/StreamRange"
-                }
-            ]
+            "$ref": "#/$defs/StreamRange"
         },
         "stream_resource": {
             "title": "Stream Resource",


Test patch:
diff --git a/event_model/tests/test_em.py b/event_model/tests/test_em.py
index 15e0fd815..015072f2d 100644
--- a/event_model/tests/test_em.py
+++ b/event_model/tests/test_em.py
@@ -56,7 +56,7 @@ def test_compose_run():
     assert bundle.compose_stop is compose_stop
     bundle = compose_descriptor(
         data_keys={
-            "motor": {"shape": [], "dtype": "number", "source": "...", "units": None},
+            "motor": {"shape": [], "dtype": "number", "source": "...", "units": "s"},
             "image": {
                 "shape": [512, 512],
                 "dtype": "number",