diff --git a/docs/conf.py b/docs/conf.py index cdfc8f45a..ed45991e6 100644 --- a/docs/conf.py +++ b/docs/conf.py @@ -71,10 +71,27 @@ ("py:class", "'object'"), ("py:class", "'id'"), ("py:class", "typing_extensions.Literal"), + # Doesn't work even with asyncio intersphinx mapping + ("py:class", "asyncio.events.AbstractEventLoop"), + ("py:class", "asyncio.streams.StreamReader"), + ("py:class", "asyncio.streams.StreamWriter"), + # Annoying error: + # docstring of collections.abc.Callable:1: WARNING: + # 'any' reference target not found: self [ref.any] + ("any", "self"), + # p4p doesn't have intersphinx mapping + ("py:class", "p4p.server.StaticProvider"), + ("py:class", "p4p.nt.scalar.NTScalar"), + ("py:class", "p4p.nt.enum.NTEnum"), + ("py:class", "p4p.nt.ndarray.NTNDArray"), + ("py:class", "p4p.nt.NTTable"), + # Problems in FastCS itself + ("py:class", "fastcs.transport.epics.pva.pvi_tree._PviSignalInfo"), + # TypeVar without docstrings still give warnings + ("py:class", "fastcs.datatypes.T_Numerical"), + ("py:class", "strawberry.schema.schema.Schema"), ] -nitpick_ignore_regex = [ - ("py:class", "fastcs.*.T"), -] +nitpick_ignore_regex = [("py:class", "fastcs.*.T")] # Both the class’ and the __init__ method’s docstring are concatenated and # inserted into the main body of the autoclass directive @@ -112,7 +129,9 @@ # This means you can link things like `str` and `asyncio` to the relevant # docs in the python documentation. -intersphinx_mapping = {"python": ("https://docs.python.org/3/", None)} +intersphinx_mapping = { + "python": ("https://docs.python.org/3/", None), +} # A dictionary of graphviz graph attributes for inheritance diagrams. inheritance_graph_attrs = {"rankdir": "TB"} diff --git a/src/fastcs/__init__.py b/src/fastcs/__init__.py index 43507e2e0..d6377d6e5 100644 --- a/src/fastcs/__init__.py +++ b/src/fastcs/__init__.py @@ -6,8 +6,10 @@ Version number as calculated by https://github.com/pypa/setuptools_scm """ -from ._version import __version__ +from . import attributes as attributes +from . import controller as controller +from . import cs_methods as cs_methods +from . import datatypes as datatypes +from . import transport as transport +from ._version import __version__ as __version__ from .launch import FastCS as FastCS -from .launch import launch as launch - -__all__ = ["__version__"] diff --git a/src/fastcs/attributes.py b/src/fastcs/attributes.py index 990be7c61..a36a00e48 100644 --- a/src/fastcs/attributes.py +++ b/src/fastcs/attributes.py @@ -5,6 +5,8 @@ from enum import Enum from typing import Any, Generic, Protocol, runtime_checkable +import fastcs + from .datatypes import ATTRIBUTE_TYPES, AttrCallback, DataType, T @@ -20,7 +22,9 @@ class AttrMode(Enum): class Sender(Protocol): """Protocol for setting the value of an ``Attribute``.""" - async def put(self, controller: Any, attr: AttrW, value: Any) -> None: + async def put( + self, controller: fastcs.controller.BaseController, attr: AttrW, value: Any + ) -> None: pass @@ -31,7 +35,9 @@ class Updater(Protocol): # If update period is None then the attribute will not be updated as a task. update_period: float | None = None - async def update(self, controller: Any, attr: AttrR) -> None: + async def update( + self, controller: fastcs.controller.BaseController, attr: AttrR + ) -> None: pass @@ -45,7 +51,9 @@ class Handler(Sender, Updater, Protocol): class SimpleHandler(Handler): """Handler for internal parameters""" - async def put(self, controller: Any, attr: AttrW, value: Any): + async def put( + self, controller: fastcs.controller.BaseController, attr: AttrW, value: Any + ): await attr.update_display_without_process(value) if isinstance(attr, AttrRW): diff --git a/src/fastcs/backend.py b/src/fastcs/backend.py index 7c500e790..b37fb7ff2 100644 --- a/src/fastcs/backend.py +++ b/src/fastcs/backend.py @@ -11,6 +11,8 @@ class Backend: + """For keeping track of tasks during FastCS serving.""" + def __init__( self, controller: Controller, @@ -163,6 +165,7 @@ async def scan_coro() -> None: def build_controller_api(controller: Controller) -> ControllerAPI: + """Build a `ControllerAPI` for a `BaseController` and its sub controllers""" return _build_controller_api(controller, []) diff --git a/src/fastcs/connections/ip_connection.py b/src/fastcs/connections/ip_connection.py index aab2ac419..9cd5e6f1b 100644 --- a/src/fastcs/connections/ip_connection.py +++ b/src/fastcs/connections/ip_connection.py @@ -3,6 +3,8 @@ class DisconnectedError(Exception): + """Raised if the ip connection is disconnected.""" + pass @@ -14,6 +16,8 @@ class IPConnectionSettings: @dataclass class StreamConnection: + """For reading and writing to a stream.""" + reader: asyncio.StreamReader writer: asyncio.StreamWriter @@ -41,6 +45,8 @@ async def close(self): class IPConnection: + """For connecting to an ip using a `StreamConnection`.""" + def __init__(self): self.__connection = None diff --git a/src/fastcs/connections/serial_connection.py b/src/fastcs/connections/serial_connection.py index 8c8e15774..65bbf6801 100644 --- a/src/fastcs/connections/serial_connection.py +++ b/src/fastcs/connections/serial_connection.py @@ -5,6 +5,8 @@ class NotOpenedError(Exception): + """If the serial stream is not opened.""" + pass @@ -15,6 +17,8 @@ class SerialConnectionSettings: class SerialConnection: + """A serial connection.""" + def __init__(self): self.stream = None self._lock = asyncio.Lock() diff --git a/src/fastcs/controller.py b/src/fastcs/controller.py index c7fbc45fa..c9351a903 100755 --- a/src/fastcs/controller.py +++ b/src/fastcs/controller.py @@ -7,6 +7,8 @@ class BaseController: + """Base class for controller.""" + #: Attributes passed from the device at runtime. attributes: dict[str, Attribute] @@ -29,7 +31,7 @@ def __init__( @property def path(self) -> list[str]: - """Path prefix of attributes, recursively including parent ``Controller``s.""" + """Path prefix of attributes, recursively including parent Controllers.""" return self._path def set_path(self, path: list[str]): diff --git a/src/fastcs/controller_api.py b/src/fastcs/controller_api.py index a9afa47f8..ac5d1af31 100644 --- a/src/fastcs/controller_api.py +++ b/src/fastcs/controller_api.py @@ -20,10 +20,10 @@ class ControllerAPI: description: str | None = None def walk_api(self) -> Iterator["ControllerAPI"]: - """Walk through all the nested `ControllerAPIs` of this `ControllerAPI` + """Walk through all the nested `ControllerAPI` s of this `ControllerAPI`. - yields: `ControllerAPI`s from a depth-first traversal of the tree, including - self. + Yields the `ControllerAPI` s from a depth-first traversal of the tree, + including self. """ yield self diff --git a/src/fastcs/cs_methods.py b/src/fastcs/cs_methods.py index fbab7e1a5..5bcd9a267 100644 --- a/src/fastcs/cs_methods.py +++ b/src/fastcs/cs_methods.py @@ -123,6 +123,8 @@ async def __call__(self): class Put(Method[BaseController]): + """Why don't know what this is for.""" + def __init__(self, fn: PutCallback): super().__init__(fn) @@ -142,7 +144,7 @@ class UnboundCommand(Method[Controller_T]): This generic class stores an unbound `Controller` method - effectively a function that takes an instance of a specific `Controller` type (`Controller_T`). Instances of this class can be added at `Controller` definition, either manually or with use - of the `@command` wrapper, to register the method to be included in the API of the + of the `command` wrapper, to register the method to be included in the API of the `Controller`. When the `Controller` is instantiated, these instances will be bound to the instance, creating a `Command` instance. """ @@ -171,7 +173,7 @@ class UnboundScan(Method[Controller_T]): This generic class stores an unbound `Controller` method - effectively a function that takes an instance of a specific `Controller` type (`Controller_T`). Instances of this class can be added at `Controller` definition, either manually or with use - of the `@scan` wrapper, to register the method to be included in the API of the + of the `scan` wrapper, to register the method to be included in the API of the `Controller`. When the `Controller` is instantiated, these instances will be bound to the instance, creating a `Scan` instance. """ @@ -199,6 +201,8 @@ def __call__(self): class UnboundPut(Method[Controller_T]): + """Unbound version of `Put`.""" + def __init__(self, fn: UnboundPutCallback[Controller_T]) -> None: super().__init__(fn) diff --git a/src/fastcs/exceptions.py b/src/fastcs/exceptions.py index 64964cbf6..b151c9f98 100644 --- a/src/fastcs/exceptions.py +++ b/src/fastcs/exceptions.py @@ -1,6 +1,8 @@ class FastCSException(Exception): - pass + """Base class for general problems in the running of a FastCS transport.""" class LaunchError(FastCSException): - pass + """For when there is an error in launching FastCS with the given + transports and controller. + """ diff --git a/src/fastcs/launch.py b/src/fastcs/launch.py index 3f4451115..0234bbe4d 100644 --- a/src/fastcs/launch.py +++ b/src/fastcs/launch.py @@ -28,6 +28,8 @@ class FastCS: + """For launching a controller with given transport(s).""" + def __init__( self, controller: Controller, @@ -249,6 +251,7 @@ def _extract_options_model(controller_class: type[Controller]) -> type[BaseModel def get_controller_schema(target: type[Controller]) -> dict[str, Any]: + """Gets schema for a give controller for serialisation.""" options_model = _extract_options_model(target) target_schema = options_model.model_json_schema() return target_schema diff --git a/src/fastcs/transport/adapter.py b/src/fastcs/transport/adapter.py index af18bd2a5..c3ed79143 100644 --- a/src/fastcs/transport/adapter.py +++ b/src/fastcs/transport/adapter.py @@ -3,6 +3,9 @@ class TransportAdapter(ABC): + """A base class for adapting a transport's implementation to + so it can be used in FastCS.""" + @property @abstractmethod def options(self) -> Any: diff --git a/src/fastcs/transport/epics/ca/adapter.py b/src/fastcs/transport/epics/ca/adapter.py index 00b8c97a5..7f514a451 100644 --- a/src/fastcs/transport/epics/ca/adapter.py +++ b/src/fastcs/transport/epics/ca/adapter.py @@ -9,6 +9,8 @@ class EpicsCATransport(TransportAdapter): + """Channel access transport.""" + def __init__( self, controller_api: ControllerAPI, diff --git a/src/fastcs/transport/epics/ca/ioc.py b/src/fastcs/transport/epics/ca/ioc.py index 447681ed4..ac9ca00d7 100644 --- a/src/fastcs/transport/epics/ca/ioc.py +++ b/src/fastcs/transport/epics/ca/ioc.py @@ -22,6 +22,11 @@ class EpicsCAIOC: + """A softioc which handles a controller. + + Avoid running directly, instead use `fastcs.launch.FastCS`. + """ + def __init__( self, pv_prefix: str, diff --git a/src/fastcs/transport/epics/ca/options.py b/src/fastcs/transport/epics/ca/options.py index 547a35108..8d642e958 100644 --- a/src/fastcs/transport/epics/ca/options.py +++ b/src/fastcs/transport/epics/ca/options.py @@ -9,6 +9,8 @@ @dataclass class EpicsCAOptions: + """Options for the EPICS CA transport.""" + docs: EpicsDocsOptions = field(default_factory=EpicsDocsOptions) gui: EpicsGUIOptions = field(default_factory=EpicsGUIOptions) ioc: EpicsIOCOptions = field(default_factory=EpicsIOCOptions) diff --git a/src/fastcs/transport/epics/ca/util.py b/src/fastcs/transport/epics/ca/util.py index aced440b4..7d8ee8d9f 100644 --- a/src/fastcs/transport/epics/ca/util.py +++ b/src/fastcs/transport/epics/ca/util.py @@ -45,10 +45,15 @@ def record_metadata_from_attribute( attribute: Attribute[T], ) -> dict[str, str | None]: + """Converts attributes on the `Attribute` to the + field name/value in the record metadata.""" return {"DESC": attribute.description} def record_metadata_from_datatype(datatype: DataType[T]) -> dict[str, str]: + """Converts attributes on the `DataType` to the + field name/value in the record metadata.""" + arguments = { DATATYPE_FIELD_TO_RECORD_FIELD[field]: value for field, value in asdict(datatype).items() @@ -78,6 +83,7 @@ def record_metadata_from_datatype(datatype: DataType[T]) -> dict[str, str]: def cast_from_epics_type(datatype: DataType[T], value: object) -> T: + """Casts from an EPICS datatype to a FastCS datatype.""" match datatype: case Enum(): return datatype.validate(datatype.members[value]) @@ -88,6 +94,7 @@ def cast_from_epics_type(datatype: DataType[T], value: object) -> T: def cast_to_epics_type(datatype: DataType[T], value: T) -> object: + """Casts from an attribute's datatype to an EPICS datatype.""" match datatype: case Enum(): return datatype.index_of(datatype.validate(value)) @@ -100,6 +107,7 @@ def cast_to_epics_type(datatype: DataType[T], value: T) -> object: def builder_callable_from_attribute( attribute: AttrR | AttrW | AttrRW, make_in_record: bool ): + """Returns a callable to make the softioc record from an attribute instance.""" match attribute.datatype: case Bool(): return builder.boolIn if make_in_record else builder.boolOut diff --git a/src/fastcs/transport/epics/docs.py b/src/fastcs/transport/epics/docs.py index 1a087d159..d4c3db8c3 100644 --- a/src/fastcs/transport/epics/docs.py +++ b/src/fastcs/transport/epics/docs.py @@ -4,6 +4,8 @@ class EpicsDocs: + """For creating docs in the EPICS transports.""" + def __init__(self, controller_apis: ControllerAPI) -> None: self._controller_apis = controller_apis diff --git a/src/fastcs/transport/epics/gui.py b/src/fastcs/transport/epics/gui.py index 5c44e5c10..d08f8ce5e 100644 --- a/src/fastcs/transport/epics/gui.py +++ b/src/fastcs/transport/epics/gui.py @@ -33,6 +33,8 @@ class EpicsGUI: + """For creating gui in the EPICS transports.""" + def __init__(self, controller_api: ControllerAPI, pv_prefix: str) -> None: self._controller_api = controller_api self._pv_prefix = pv_prefix diff --git a/src/fastcs/transport/epics/options.py b/src/fastcs/transport/epics/options.py index c42177c11..029d634c6 100644 --- a/src/fastcs/transport/epics/options.py +++ b/src/fastcs/transport/epics/options.py @@ -5,17 +5,23 @@ @dataclass class EpicsDocsOptions: + """Docs options for EPICS.""" + path: Path = Path(".") depth: int | None = None class EpicsGUIFormat(Enum): + """The format of an EPICS GUI.""" + bob = ".bob" edl = ".edl" @dataclass class EpicsGUIOptions: + """Epics GUI options for use in both CA and PVA transports.""" + output_path: Path = Path(".") / "output.bob" file_format: EpicsGUIFormat = EpicsGUIFormat.bob title: str = "Simple Device" @@ -23,4 +29,6 @@ class EpicsGUIOptions: @dataclass class EpicsIOCOptions: + """Epics IOC options for use in both CA and PVA transports.""" + pv_prefix: str = "MY-DEVICE-PREFIX" diff --git a/src/fastcs/transport/epics/pva/adapter.py b/src/fastcs/transport/epics/pva/adapter.py index b3d34f3f1..3df0653f7 100644 --- a/src/fastcs/transport/epics/pva/adapter.py +++ b/src/fastcs/transport/epics/pva/adapter.py @@ -8,6 +8,8 @@ class EpicsPVATransport(TransportAdapter): + """PV access transport.""" + def __init__( self, controller_api: ControllerAPI, diff --git a/src/fastcs/transport/epics/pva/ioc.py b/src/fastcs/transport/epics/pva/ioc.py index 18655211a..923dc618e 100644 --- a/src/fastcs/transport/epics/pva/ioc.py +++ b/src/fastcs/transport/epics/pva/ioc.py @@ -30,6 +30,7 @@ def _snake_to_pascal(name: str) -> str: def get_pv_name(pv_prefix: str, *attribute_names: str) -> str: + """Converts from an attribute name to a pv name.""" pv_formatted = ":".join([_snake_to_pascal(attr) for attr in attribute_names]) return f"{pv_prefix}:{pv_formatted}" if pv_formatted else pv_prefix @@ -37,6 +38,7 @@ def get_pv_name(pv_prefix: str, *attribute_names: str) -> str: async def parse_attributes( root_pv_prefix: str, root_controller_api: ControllerAPI ) -> list[StaticProvider]: + """Parses `Attribute` s into p4p signals in handlers.""" pvi_tree = PviTree(root_pv_prefix) provider = StaticProvider(root_pv_prefix) @@ -61,6 +63,11 @@ async def parse_attributes( class P4PIOC: + """A P4P IOC which handles a controller. + + Avoid running directly, instead use `fastcs.launch.FastCS`. + """ + def __init__(self, pv_prefix: str, controller_api: ControllerAPI): self.pv_prefix = pv_prefix self.controller_api = controller_api diff --git a/src/fastcs/transport/epics/pva/options.py b/src/fastcs/transport/epics/pva/options.py index e9ee9fb26..4d884fd9b 100644 --- a/src/fastcs/transport/epics/pva/options.py +++ b/src/fastcs/transport/epics/pva/options.py @@ -1,6 +1,6 @@ from dataclasses import dataclass, field -from ..options import ( +from fastcs.transport.epics.options import ( EpicsDocsOptions, EpicsGUIOptions, EpicsIOCOptions, @@ -9,6 +9,8 @@ @dataclass class EpicsPVAOptions: + """Options for the EPICS PVA transport.""" + docs: EpicsDocsOptions = field(default_factory=EpicsDocsOptions) gui: EpicsGUIOptions = field(default_factory=EpicsGUIOptions) ioc: EpicsIOCOptions = field(default_factory=EpicsIOCOptions) diff --git a/src/fastcs/transport/epics/pva/pvi_tree.py b/src/fastcs/transport/epics/pva/pvi_tree.py index dbe405b11..e4f8726ff 100644 --- a/src/fastcs/transport/epics/pva/pvi_tree.py +++ b/src/fastcs/transport/epics/pva/pvi_tree.py @@ -17,6 +17,8 @@ @dataclass class _PviSignalInfo: + """For storing a pv and it's access in pvi parsing.""" + pv: str access: AccessModeType @@ -35,6 +37,8 @@ def _pv_to_pvi_name(pv: str) -> tuple[str, int | None]: class PviDevice(dict[str, "PviDevice"]): + """For creating a pvi structure in pva.""" + pv_prefix: str description: str | None device_signal_info: _PviSignalInfo | None @@ -166,6 +170,8 @@ def make_provider( # TODO: This can be dramatically cleaned up after https://github.com/DiamondLightSource/FastCS/issues/122 class PviTree: + """For storing pvi structures.""" + def __init__(self, pv_prefix: str): self._pvi_tree_root: PviDevice = PviDevice(pv_prefix) diff --git a/src/fastcs/transport/epics/pva/types.py b/src/fastcs/transport/epics/pva/types.py index 94f1c8048..08a83b1ea 100644 --- a/src/fastcs/transport/epics/pva/types.py +++ b/src/fastcs/transport/epics/pva/types.py @@ -51,6 +51,7 @@ def _table_with_numpy_dtypes_to_p4p_dtypes(numpy_dtypes: list[tuple[str, DTypeLi def make_p4p_type( attribute: Attribute, ) -> NTScalar | NTEnum | NTNDArray | NTTable: + """Creates a p4p type for a given `Attribute` s `fastcs.datatypes.DataType`.""" display = isinstance(attribute, AttrR) control = isinstance(attribute, AttrW) match attribute.datatype: @@ -81,10 +82,11 @@ def make_p4p_type( columns=_table_with_numpy_dtypes_to_p4p_dtypes(structured_dtype) ) case _: - raise RuntimeError(f"Datatype `{attribute.datatype}` unsupported in P4P.") + raise RuntimeError(f"DataType `{attribute.datatype}` unsupported in P4P.") def cast_from_p4p_value(attribute: Attribute[T], value: object) -> T: + """Converts from a p4p value to a FastCS `Attribute` value.""" match attribute.datatype: case Enum(): return attribute.datatype.validate(attribute.datatype.members[value.index]) @@ -108,6 +110,7 @@ def p4p_alarm_states( status: int = NO_ALARM_STATUS, message: str = "", ) -> dict: + """Returns the p4p alarm structure for a given severity, status, and message.""" return { "alarm": { "severity": severity, @@ -118,6 +121,7 @@ def p4p_alarm_states( def p4p_timestamp_now() -> dict: + """The p4p timestamp structure for the current time.""" now = time.time() seconds_past_epoch = int(now) nanoseconds = int((now - seconds_past_epoch) * 1e9) @@ -130,6 +134,7 @@ def p4p_timestamp_now() -> dict: def p4p_display(attribute: Attribute) -> dict: + """Gets the p4p display structure for a given attribute.""" display = {} if attribute.description is not None: display["description"] = attribute.description @@ -172,6 +177,8 @@ def _p4p_check_numerical_for_alarm_states(datatype: Int | Float, value: T) -> di def cast_to_p4p_value(attribute: Attribute[T], value: T) -> object: + """Converts a FastCS `Attribute` value to a p4p value, + including metadata and alarm states.""" match attribute.datatype: case Enum(): return { diff --git a/src/fastcs/transport/graphQL/adapter.py b/src/fastcs/transport/graphQL/adapter.py index 79853f2f7..b59deb9d3 100644 --- a/src/fastcs/transport/graphQL/adapter.py +++ b/src/fastcs/transport/graphQL/adapter.py @@ -6,6 +6,8 @@ class GraphQLTransport(TransportAdapter): + """GraphQL transport.""" + def __init__( self, controller_api: ControllerAPI, diff --git a/src/fastcs/transport/graphQL/graphQL.py b/src/fastcs/transport/graphQL/graphQL.py index 1be19d3b3..69743701c 100644 --- a/src/fastcs/transport/graphQL/graphQL.py +++ b/src/fastcs/transport/graphQL/graphQL.py @@ -15,6 +15,11 @@ class GraphQLServer: + """A GraphQL server which handles a controller. + + Avoid running directly, instead use `fastcs.launch.FastCS`. + """ + def __init__(self, controller_api: ControllerAPI): self._controller_api = controller_api self._app = self._create_app() diff --git a/src/fastcs/transport/graphQL/options.py b/src/fastcs/transport/graphQL/options.py index b1ce2e83f..3d8e85199 100644 --- a/src/fastcs/transport/graphQL/options.py +++ b/src/fastcs/transport/graphQL/options.py @@ -10,4 +10,6 @@ class GraphQLServerOptions: @dataclass class GraphQLOptions: + """Options for the GraphQL transport.""" + gql: GraphQLServerOptions = field(default_factory=GraphQLServerOptions) diff --git a/src/fastcs/transport/rest/adapter.py b/src/fastcs/transport/rest/adapter.py index cac64ae49..130c73480 100644 --- a/src/fastcs/transport/rest/adapter.py +++ b/src/fastcs/transport/rest/adapter.py @@ -6,6 +6,8 @@ class RestTransport(TransportAdapter): + """Rest Transport Adapter.""" + def __init__( self, controller_api: ControllerAPI, diff --git a/src/fastcs/transport/rest/options.py b/src/fastcs/transport/rest/options.py index 32ecd6f24..7058025e5 100644 --- a/src/fastcs/transport/rest/options.py +++ b/src/fastcs/transport/rest/options.py @@ -10,4 +10,6 @@ class RestServerOptions: @dataclass class RestOptions: + """Options for the Rest transport.""" + rest: RestServerOptions = field(default_factory=RestServerOptions) diff --git a/src/fastcs/transport/rest/rest.py b/src/fastcs/transport/rest/rest.py index 5fcadc238..db31247c5 100644 --- a/src/fastcs/transport/rest/rest.py +++ b/src/fastcs/transport/rest/rest.py @@ -18,6 +18,11 @@ class RestServer: + """A Rest Server which handles a controller. + + Avoid running directly, instead use `fastcs.launch.FastCS`. + """ + def __init__(self, controller_api: ControllerAPI): self._controller_api = controller_api self._app = self._create_app() diff --git a/src/fastcs/transport/rest/util.py b/src/fastcs/transport/rest/util.py index 4c71a0e68..e714338b3 100644 --- a/src/fastcs/transport/rest/util.py +++ b/src/fastcs/transport/rest/util.py @@ -6,6 +6,7 @@ def convert_datatype(datatype: DataType[T]) -> type: + """Converts a datatype to a rest serialisable type.""" match datatype: case Waveform(): return list @@ -14,6 +15,7 @@ def convert_datatype(datatype: DataType[T]) -> type: def cast_to_rest_type(datatype: DataType[T], value: T) -> object: + """Casts from an attribute value to a rest value.""" match datatype: case Waveform(): return value.tolist() @@ -24,6 +26,7 @@ def cast_to_rest_type(datatype: DataType[T], value: T) -> object: def cast_from_rest_type(datatype: DataType[T], value: object) -> T: + """Casts from a rest value to an attribute datatype.""" match datatype: case Waveform(): return datatype.validate(np.array(value, dtype=datatype.array_dtype)) diff --git a/src/fastcs/transport/tango/adapter.py b/src/fastcs/transport/tango/adapter.py index 96938f7b9..283adabcb 100644 --- a/src/fastcs/transport/tango/adapter.py +++ b/src/fastcs/transport/tango/adapter.py @@ -8,6 +8,8 @@ class TangoTransport(TransportAdapter): + """Tango transport.""" + def __init__( self, controller_api: ControllerAPI, diff --git a/src/fastcs/transport/tango/dsr.py b/src/fastcs/transport/tango/dsr.py index cfefc9cbe..7686ef36d 100644 --- a/src/fastcs/transport/tango/dsr.py +++ b/src/fastcs/transport/tango/dsr.py @@ -168,6 +168,11 @@ def _collect_dsr_args(options: TangoDSROptions) -> list[str]: class TangoDSR: + """For controlling a controller with tango. + + Avoid running directly, instead use `fastcs.launch.FastCS`. + """ + def __init__( self, controller_api: ControllerAPI, @@ -205,6 +210,7 @@ def run(self, options: TangoDSROptions | None = None) -> None: def register_dev(dev_name: str, dev_class: str, dsr_instance: str) -> None: + """Register a device instance in the tango server.""" dsr_name = f"{dev_class}/{dsr_instance}" dev_info = DbDevInfo(dev_name, dev_class, dsr_name) diff --git a/src/fastcs/transport/tango/options.py b/src/fastcs/transport/tango/options.py index 850393eb7..44b5db676 100644 --- a/src/fastcs/transport/tango/options.py +++ b/src/fastcs/transport/tango/options.py @@ -10,4 +10,6 @@ class TangoDSROptions: @dataclass class TangoOptions: + """Options for the Tango transport.""" + dsr: TangoDSROptions = field(default_factory=TangoDSROptions) diff --git a/src/fastcs/transport/tango/util.py b/src/fastcs/transport/tango/util.py index 37ea0f301..c60e1a431 100644 --- a/src/fastcs/transport/tango/util.py +++ b/src/fastcs/transport/tango/util.py @@ -20,12 +20,14 @@ def get_server_metadata_from_attribute( attribute: Attribute[T], ) -> dict[str, Any]: + """Gets the metadata for a Tango field from an attribute.""" arguments = {} arguments["doc"] = attribute.description if attribute.description else "" return arguments def get_server_metadata_from_datatype(datatype: DataType[T]) -> dict[str, str]: + """Gets the metadata for a Tango field from a FastCS datatype.""" arguments = { DATATYPE_FIELD_TO_SERVER_FIELD[field]: value for field, value in asdict(datatype).items() @@ -61,6 +63,7 @@ def get_server_metadata_from_datatype(datatype: DataType[T]) -> dict[str, str]: def cast_to_tango_type(datatype: DataType[T], value: T) -> object: + """Casts a value from FastCS to tango datatype.""" match datatype: case Enum(): return datatype.index_of(datatype.validate(value)) @@ -71,6 +74,7 @@ def cast_to_tango_type(datatype: DataType[T], value: T) -> object: def cast_from_tango_type(datatype: DataType[T], value: object) -> T: + """Casts a value from tango to FastCS datatype.""" match datatype: case Enum(): return datatype.validate(datatype.members[value]) diff --git a/src/fastcs/wrappers.py b/src/fastcs/wrappers.py index 41b3a5c02..4462a559d 100644 --- a/src/fastcs/wrappers.py +++ b/src/fastcs/wrappers.py @@ -15,6 +15,8 @@ def scan( period: float, ) -> Callable[[UnboundScanCallback[Controller_T]], UnboundScan[Controller_T]]: + """Sets up a scan over the wrapped method.""" + if period <= 0: raise FastCSException("Scan method must have a positive scan period") @@ -25,6 +27,7 @@ def wrapper(fn: UnboundScanCallback[Controller_T]) -> UnboundScan[Controller_T]: def put(fn: UnboundPutCallback[Controller_T]) -> UnboundPut[Controller_T]: + """Sets up a put over the wrapped method.""" return UnboundPut(fn) diff --git a/tests/data/schema.json b/tests/data/schema.json index d3dc5bc0f..0057af1e2 100644 --- a/tests/data/schema.json +++ b/tests/data/schema.json @@ -40,6 +40,7 @@ "type": "object" }, "EpicsGUIFormat": { + "description": "The format of an EPICS GUI.", "enum": [ ".bob", ".edl"