Skip to content

Commit 89ea1a5

Browse files
committed
Fix Transport imports
Make import of all transports conditional Build Union of Transports that get imported Error if no imports are available for now
1 parent 9879f4e commit 89ea1a5

File tree

3 files changed

+59
-30
lines changed

3 files changed

+59
-30
lines changed

src/fastcs/launch.py

Lines changed: 17 additions & 20 deletions
Original file line numberDiff line numberDiff line change
@@ -6,11 +6,11 @@
66
from collections.abc import Callable, Coroutine, Sequence
77
from functools import partial
88
from pathlib import Path
9-
from typing import Annotated, Any, Optional, TypeAlias, get_type_hints
9+
from typing import Annotated, Any, Optional, get_type_hints
1010

1111
import typer
1212
from IPython.terminal.embed import InteractiveShellEmbed
13-
from pydantic import BaseModel, create_model
13+
from pydantic import BaseModel, ValidationError, create_model
1414
from ruamel.yaml import YAML
1515

1616
from fastcs import __version__
@@ -26,11 +26,6 @@
2626
)
2727
from fastcs.logging import logger as _fastcs_logger
2828
from fastcs.tracer import Tracer
29-
from fastcs.transport.epics.ca.transport import EpicsCATransport
30-
from fastcs.transport.epics.pva.transport import EpicsPVATransport
31-
from fastcs.transport.graphql.transport import GraphQLTransport
32-
from fastcs.transport.rest.transport import RestTransport
33-
from fastcs.transport.tango.transport import TangoTransport
3429

3530
from .attributes import ONCE, AttrR, AttrW
3631
from .controller import BaseController, Controller
@@ -41,15 +36,6 @@
4136
from .transport import Transport
4237
from .util import validate_hinted_attributes
4338

44-
# Define a type alias for transport options
45-
TransportList: TypeAlias = list[
46-
EpicsPVATransport
47-
| EpicsCATransport
48-
| TangoTransport
49-
| RestTransport
50-
| GraphQLTransport
51-
]
52-
5339
tracer = Tracer(name=__name__)
5440
logger = _fastcs_logger.bind(logger_name=__name__)
5541

@@ -440,8 +426,19 @@ def run(
440426

441427
yaml = YAML(typ="safe")
442428
options_yaml = yaml.load(config)
443-
# To do: Handle a k8s "values.yaml" file
444-
instance_options = fastcs_options.model_validate(options_yaml)
429+
430+
try:
431+
instance_options = fastcs_options.model_validate(options_yaml)
432+
except ValidationError as e:
433+
if any("transport" in error["loc"] for error in json.loads(e.json())):
434+
raise LaunchError(
435+
"Failed to validate transports. "
436+
"Are the correct fastcs extras installed? "
437+
f"Available transports:\n{Transport.subclasses}",
438+
) from e
439+
440+
raise LaunchError("Failed to validate config") from e
441+
445442
if hasattr(instance_options, "controller"):
446443
controller = controller_class(instance_options.controller)
447444
else:
@@ -466,7 +463,7 @@ def _extract_options_model(controller_class: type[Controller]) -> type[BaseModel
466463
if len(args) == 1:
467464
fastcs_options = create_model(
468465
f"{controller_class.__name__}",
469-
transport=(TransportList, ...),
466+
transport=(list[Transport.union()], ...),
470467
__config__={"extra": "forbid"},
471468
)
472469
elif len(args) == 2:
@@ -483,7 +480,7 @@ def _extract_options_model(controller_class: type[Controller]) -> type[BaseModel
483480
fastcs_options = create_model(
484481
f"{controller_class.__name__}",
485482
controller=(options_type, ...),
486-
transport=(TransportList, ...),
483+
transport=(list[Transport.union()], ...),
487484
__config__={"extra": "forbid"},
488485
)
489486
else:

src/fastcs/transport/__init__.py

Lines changed: 29 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -1,10 +1,30 @@
1-
from .epics.ca.transport import EpicsCATransport as EpicsCATransport
2-
from .epics.options import EpicsDocsOptions as EpicsDocsOptions
3-
from .epics.options import EpicsGUIOptions as EpicsGUIOptions
4-
from .epics.options import EpicsIOCOptions as EpicsIOCOptions
5-
from .epics.pva.transport import EpicsPVATransport as EpicsPVATransport
6-
from .graphql.transport import GraphQLTransport as GraphQLTransport
7-
from .rest.transport import RestTransport as RestTransport
8-
from .tango.options import TangoDSROptions as TangoDSROptions
9-
from .tango.transport import TangoTransport as TangoTransport
101
from .transport import Transport as Transport
2+
3+
try:
4+
from .epics.ca.transport import EpicsCATransport as EpicsCATransport
5+
from .epics.options import EpicsDocsOptions as EpicsDocsOptions
6+
from .epics.options import EpicsGUIOptions as EpicsGUIOptions
7+
from .epics.options import EpicsIOCOptions as EpicsIOCOptions
8+
except ImportError:
9+
pass
10+
11+
try:
12+
from .epics.pva.transport import EpicsPVATransport as EpicsPVATransport
13+
except ImportError:
14+
pass
15+
16+
try:
17+
from .graphql.transport import GraphQLTransport as GraphQLTransport
18+
except ImportError:
19+
pass
20+
21+
try:
22+
from .rest.transport import RestTransport as RestTransport
23+
except ImportError:
24+
pass
25+
26+
try:
27+
from .tango.options import TangoDSROptions as TangoDSROptions
28+
from .tango.transport import TangoTransport as TangoTransport
29+
except ImportError:
30+
pass

src/fastcs/transport/transport.py

Lines changed: 13 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
11
import asyncio
22
from abc import abstractmethod
33
from dataclasses import dataclass
4-
from typing import Any
4+
from typing import Any, ClassVar, Union
55

66
from fastcs.controller_api import ControllerAPI
77

@@ -11,6 +11,18 @@ class Transport:
1111
"""A base class for transport's implementation
1212
so it can be used in FastCS."""
1313

14+
subclasses: ClassVar[list[type["Transport"]]] = []
15+
16+
def __init_subclass__(cls):
17+
cls.subclasses.append(cls)
18+
19+
@classmethod
20+
def union(cls):
21+
if not cls.subclasses:
22+
raise RuntimeError("No Transports found")
23+
24+
return Union[tuple(cls.subclasses)] # noqa: UP007
25+
1426
@abstractmethod
1527
async def serve(self) -> None:
1628
pass

0 commit comments

Comments
 (0)