Skip to content

Commit

Permalink
remove the nasty root for discriminated union
Browse files Browse the repository at this point in the history
  • Loading branch information
gilesknap committed Oct 13, 2023
1 parent 424e5f3 commit 8d2f575
Show file tree
Hide file tree
Showing 9 changed files with 50 additions and 97 deletions.
14 changes: 8 additions & 6 deletions src/ibek/ioc.py
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,12 @@
from enum import Enum
from typing import Any, Dict, Literal, Sequence, Tuple, Type, Union

from pydantic import Field, RootModel, create_model, field_validator, model_validator
from pydantic import (
Field,
create_model,
field_validator,
model_validator,
)
from pydantic.fields import FieldInfo
from pydantic_core import PydanticUndefined

Expand Down Expand Up @@ -166,12 +171,9 @@ def make_ioc_model(entity_models: Sequence[Type[Entity]]) -> Type[IOC]:
be of type 'list of Entity derived classes'.
"""

class EntityModel(RootModel):
root: Union[tuple(entity_models)] = Field(discriminator="type") # type: ignore

class NewIOC(IOC):
entities: Sequence[EntityModel] = Field( # type: ignore
description="List of entities this IOC instantiates"
entities: Sequence[Union[tuple(entity_models)]] = Field( # type: ignore
description="List of entities this IOC instantiates",
)

return NewIOC
Expand Down
7 changes: 2 additions & 5 deletions src/ibek/render.py
Original file line number Diff line number Diff line change
Expand Up @@ -108,11 +108,8 @@ def render_elements(
"""
elements = ""
for entity in ioc.entities:
# TODO can we eliminate the need for intermediate root
# see definition of EntityModel in ioc.py
instance = getattr(entity, "root")
if instance.entity_enabled:
element = method(instance)
if entity.entity_enabled:
element = method(entity)
if element:
elements += element
return elements
Expand Down
11 changes: 1 addition & 10 deletions src/ibek/render_db.py
Original file line number Diff line number Diff line change
Expand Up @@ -54,16 +54,7 @@ def parse_instances(self) -> None:
Gather the database template instantiations from all entities
while validating the arguments
"""
for entity_root in self.ioc_instance.entities:
# TODO this is a highly suspect way of coping with the difference
# between deserialized entities and the original entity class.
# The deserializer inserts an extra layer of nesting
# with "root". This issue comes up because test_database_render
# directly creates an IOC object and does not get the extra "root".
#
# see definition of EntityModel in ioc.py
entity = getattr(entity_root, "root", entity_root)

for entity in self.ioc_instance.entities:
templates = entity.__definition__.databases

# Not all entities instantiate database templates
Expand Down
2 changes: 1 addition & 1 deletion src/ibek/runtime_cmds/commands.py
Original file line number Diff line number Diff line change
Expand Up @@ -39,7 +39,7 @@ def generate(
# and also make a bob index file of buttons
#
# you can access the 'opi' definition like this:
# ioc_instance.entities[0].root.__definition__.opis()
# ioc_instance.entities[0].__definition__.opis()

ioc_instance = ioc_deserialize(instance, definitions)

Expand Down
41 changes: 14 additions & 27 deletions tests/samples/schemas/multiple.ibek.ioc.schema.json
Original file line number Diff line number Diff line change
@@ -1,31 +1,5 @@
{
"$defs": {
"EntityModel": {
"discriminator": {
"mapping": {
"module.AllObject": "#/$defs/module_AllObject",
"object_module.Consumer": "#/$defs/object_module_Consumer",
"object_module.ConsumerTwo": "#/$defs/object_module_ConsumerTwo",
"object_module.RefObject": "#/$defs/object_module_RefObject"
},
"propertyName": "type"
},
"oneOf": [
{
"$ref": "#/$defs/object_module_RefObject"
},
{
"$ref": "#/$defs/object_module_Consumer"
},
{
"$ref": "#/$defs/object_module_ConsumerTwo"
},
{
"$ref": "#/$defs/module_AllObject"
}
],
"title": "EntityModel"
},
"clock_rate": {
"enum": [
"1Hz",
Expand Down Expand Up @@ -264,7 +238,20 @@
"entities": {
"description": "List of entities this IOC instantiates",
"items": {
"$ref": "#/$defs/EntityModel"
"anyOf": [
{
"$ref": "#/$defs/object_module_RefObject"
},
{
"$ref": "#/$defs/object_module_Consumer"
},
{
"$ref": "#/$defs/object_module_ConsumerTwo"
},
{
"$ref": "#/$defs/module_AllObject"
}
]
},
"title": "Entities",
"type": "array"
Expand Down
34 changes: 11 additions & 23 deletions tests/samples/schemas/objects.ibek.ioc.schema.json
Original file line number Diff line number Diff line change
@@ -1,27 +1,5 @@
{
"$defs": {
"EntityModel": {
"discriminator": {
"mapping": {
"object_module.Consumer": "#/$defs/object_module_Consumer",
"object_module.ConsumerTwo": "#/$defs/object_module_ConsumerTwo",
"object_module.RefObject": "#/$defs/object_module_RefObject"
},
"propertyName": "type"
},
"oneOf": [
{
"$ref": "#/$defs/object_module_RefObject"
},
{
"$ref": "#/$defs/object_module_Consumer"
},
{
"$ref": "#/$defs/object_module_ConsumerTwo"
}
],
"title": "EntityModel"
},
"object_module_Consumer": {
"additionalProperties": false,
"properties": {
Expand Down Expand Up @@ -141,7 +119,17 @@
"entities": {
"description": "List of entities this IOC instantiates",
"items": {
"$ref": "#/$defs/EntityModel"
"anyOf": [
{
"$ref": "#/$defs/object_module_RefObject"
},
{
"$ref": "#/$defs/object_module_Consumer"
},
{
"$ref": "#/$defs/object_module_ConsumerTwo"
}
]
},
"title": "Entities",
"type": "array"
Expand Down
27 changes: 8 additions & 19 deletions tests/samples/schemas/utils.ibek.ioc.schema.json
Original file line number Diff line number Diff line change
@@ -1,23 +1,5 @@
{
"$defs": {
"EntityModel": {
"discriminator": {
"mapping": {
"epics.InterruptVectorVME": "#/$defs/epics_InterruptVectorVME",
"epics.InterruptVectorVME2": "#/$defs/epics_InterruptVectorVME2"
},
"propertyName": "type"
},
"oneOf": [
{
"$ref": "#/$defs/epics_InterruptVectorVME"
},
{
"$ref": "#/$defs/epics_InterruptVectorVME2"
}
],
"title": "EntityModel"
},
"epics_InterruptVectorVME": {
"additionalProperties": false,
"properties": {
Expand Down Expand Up @@ -100,7 +82,14 @@
"entities": {
"description": "List of entities this IOC instantiates",
"items": {
"$ref": "#/$defs/EntityModel"
"anyOf": [
{
"$ref": "#/$defs/epics_InterruptVectorVME"
},
{
"$ref": "#/$defs/epics_InterruptVectorVME2"
}
]
},
"title": "Entities",
"type": "array"
Expand Down
1 change: 0 additions & 1 deletion tests/test_error.py
Original file line number Diff line number Diff line change
Expand Up @@ -111,4 +111,3 @@ def test_defaults(tmp_path: Path, samples: Path):
run_cli("runtime", "generate", entity_file, definition_file1)

assert "Field required [type=missing" in str(ctx.value)
assert "entities.0.`object_module.RefObject`.name" in str(ctx.value)
10 changes: 5 additions & 5 deletions tests/test_unit.py
Original file line number Diff line number Diff line change
Expand Up @@ -48,8 +48,8 @@ def test_object_references():
)
ioc = ioc_model(**d)
port, device = ioc.entities
# TODO try to get rid of the need for '.root'
assert port.root.type == "mymodule.port"
assert device.root.type == "mymodule.device"
assert device.root.port is port.root
assert id_to_entity == {"PORT": port.root}
# TODO try to get rid of the need for ''
assert port.type == "mymodule.port"
assert device.type == "mymodule.device"
assert device.port is port
assert id_to_entity == {"PORT": port}

0 comments on commit 8d2f575

Please sign in to comment.