Skip to content

redsun.services

Servers a session's devices talk to, and the processes behind them.

A session declares services with AsService and makes a Service for each. A launched service is a child process the session starts and stops; an attached one already runs elsewhere and only lends its devices their prefix. A service process takes part in a session through the functions here: its identity, readiness, logging, and when to stop.

STARTUP_TIMEOUT module-attribute

STARTUP_TIMEOUT: Final = 15.0

Seconds a launched service has to print its readiness line, unless the service gives its own.

STOP_TIMEOUT module-attribute

STOP_TIMEOUT: Final = 10.0

Seconds each step of Service.stop waits, unless the service gives its own.

Service

Service(
    name: str,
    prefix: str = "",
    module: str | None = None,
    args: Sequence[str] | Mapping[str, ArgValue] = (),
    ready: str | None = None,
    stop_timeout: float = STOP_TIMEOUT,
    transport: str = CHANNEL_ACCESS,
    address: str | None = None,
    startup_timeout: float = STARTUP_TIMEOUT,
)

A server devices talk to, and its process if the session owns it.

A container makes one per declared service. A service with a module is launched as python -m <module> <args>; one without is attached to, runs elsewhere, and start and stop do nothing. Each output line is logged under redsun.service.<name>: as the record it describes if it is a JSON log record, at DEBUG otherwise; see service_record.

Parameters:

Name Type Description Default
name str

Name of the service.

required
prefix str

Prefix given to each device naming the service.

''
module str | None

Module to run. None attaches to a service that is already running.

None
args Sequence[str] | Mapping[str, ArgValue]

Arguments following the module: a list, or a mapping of option names to values, which -- is put before.

()
ready str | None

Text of the output line marking the service ready. None counts it ready once its process starts.

None
stop_timeout float

Seconds each step of stop waits for the process to exit.

STOP_TIMEOUT
transport str

Protocol the service is reached over: channel-access, pv-access or sharedbox. The session settles it for every service it holds.

CHANNEL_ACCESS
address str | None

Where an attached service answers, added to this process's address list when it starts. None keeps the list as the environment gives it.

None
startup_timeout float

Seconds start waits for the readiness line.

STARTUP_TIMEOUT

Raises:

Type Description
TypeError

If args are given without a module, or as text, or an address with a module.

ValueError

If transport is not a protocol a session accepts.

sig_exited class-attribute instance-attribute

sig_exited = Signal(str, int)

Emitted with the name and exit code when a ready service exits unasked.

It is emitted on the thread reading the service's output, service-<name>.

launched property

launched: bool

Whether the session launches this service rather than attaching to it.

running property

running: bool

Whether the process this service launched is still running.

start

start() -> None

Launch the service and wait until it prints its readiness line.

The process runs without a console window on Windows, and its transport gives it the environment it is reached on and tells this process where to look, so devices find it among several local services. What a transport reserves lasts for every start in this process. The process learns its name, prefix, ready text and the level this session records at through the functions of redsun.services, so a module serving several sessions needs no arguments for them. The process writes UTF-8, and each output line is logged as service_record rebuilds it. An attached service only adds its address to this process's list, if it names one.

Raises:

Type Description
TimeoutError

If the readiness line does not appear within startup_timeout; the process is stopped and its last output logged.

RuntimeError

If the process exits before it is ready.

stop

stop() -> None

Stop the launched process, escalating until it exits.

First its standard input is closed, the one request that lets a service clean up on every platform. On POSIX a service still running after stop_timeout gets SIGINT; one still running after that, or after the first step on Windows, is killed.

ServiceIdentity dataclass

ServiceIdentity(name: str, prefix: str)

What a session tells a service process it launched.

name instance-attribute

name: str

Name the session declares the service under.

prefix instance-attribute

prefix: str

Prefix given to each device naming the service; empty when none.

identity

identity() -> ServiceIdentity | None

Return the name and prefix the session gave this process.

None when no session launched it, so a service run on its own falls back on defaults of its own.

ready

ready() -> None

Print the text the session waits for before it counts this service ready.

The text is the one the service's declaration gives. Without one, nothing is printed: the session does not wait then.

ready_when_reachable async

ready_when_reachable(pv: str) -> None

Call ready once pv answers over PVAccess.

For a server that prints no line of its own once it serves. pv is looked for on the loopback, where a launched service listens; it needs p4p, which a service serving PVAccess already has. A failed attempt other than a timeout is logged as a warning and retried.

serve_controller async

serve_controller(
    controller: Controller,
    transport: Transport,
    prefix: str | None = None,
) -> None

Serve controller over transport until the session asks this service to stop.

prefix defaults to the one the session gave this process, and one trailing colon is taken off it, so a device given STAGE: finds a controller served as STAGE. The service is ready once its transport is reachable: a transport with a reachable method is awaited on it, any other is asked for the PV <prefix>:PVI.

Raises:

Type Description
ValueError

If there is no prefix, given or from a session.

wait_for_stop async

wait_for_stop() -> None

Return once the session asks this service to stop.

The session asks by closing standard input, which a daemon thread watches, so a service ending for another reason is not held up by it. When no session launched the process, this waits until cancelled and watches nothing: standard input may be closed from the start there.

stop_on_request

stop_on_request() -> None

Raise SIGINT in this process once the session asks it to stop.

For a server whose run blocks: it shuts down as it would on Ctrl+C. Does nothing when no session launched the process.

The request can come as soon as the session reads the ready text, so a service that prints it with ready and handles the interrupt itself calls ready inside the block that handles it.

configure_logging

configure_logging() -> None

Log at the session's level, in lines the session rebuilds into records.

Records of logging and, when it is installed, of loguru go to standard output as JSON, each keeping its level, time, logger name and traceback. The level is the one the session records at, INFO when no session launched the process. The handlers already on the root logger, the redsun logger and loguru are replaced, so none of their records is written twice.